Skip to content

Replace unsafe/legacy types in ui-kit/form-controls autocomplete family (#728) - #755

Merged
fpigeonjr merged 4 commits into
masterfrom
gh-728-replace-unsafe-legacy-types-in-ui-kit-form-con
Oct 1, 2026
Merged

fpigeonjr merged 4 commits into
masterfrom
gh-728-replace-unsafe-legacy-types-in-ui-kit-form-con

Conversation

@fpigeonjr

Copy link
Copy Markdown
Contributor

Description

Replaces any, unsafe function types, and legacy untyped callbacks across the autocomplete component family in src/ui-kit/form-controls, following the approach established in #706 and #732.

Scope:

  • autocomplete/autocomplete.component.ts: introduced AutocompleteItem (Record<string, unknown>), typed options/categories/enterEvent/httpRequest/cache/keyEvents/propogateChange/onKeydown/setSelected/writeValue/registerOnChange against string | AutocompleteItem instead of any; NG_VALUE_ACCESSOR typed as Provider
  • autocomplete/autocomplete.service.ts: setFetchMethod/fetch return types narrowed from any to void/unknown[]
  • autocomplete-multiselect/autocomplete-cache.ts: Cached/AutocompleteCache made generic (<T = unknown>)
  • autocomplete-multiselect/autocomplete-multiselect.component.ts: introduced MultiselectItem and CategorizedList<T>; typed options/categories/serviceOptions/itemTemplate/writeValue/registerOnChange and NodeList selection helpers
  • sam-sds-autocomplete/{autocomplete,autocomplete-search,selected-result}: NG_VALUE_ACCESSOR typed as Provider; writeValue/registerOnChange/registerOnTouched/propogateChange narrowed to unknown/typed callbacks; TemplateRef<unknown>; getFlatElements/checkItemSelected typed
  • sds-selected-item-model-helper.ts: typed clearItems(model: SAMSDSSelectedItemModel)
  • autocomplete-seach-test-service.spec.ts & autocomplete-search.component.spec.ts: removed any from test fixtures, introduced HierarchicalDataItem
  • formly/components/autocomplete/test.service.ts: updated to match AutocompleteService contract
  • eslint-baseline.json: ratcheted down warning baseline from 143 to 44 (0 errors)

Motivation and Context

Closes #728

Part of the tech debt burndown for ESLint and TypeScript safety (#580, #586). Resolves unsafe-type lint findings across the autocomplete family while preserving public API and consumer-compiled compatibility.

Type of Change (Select One and Apply Label)

  • Documentation / configuration update → Apply maintenance label (applied tech-debt)

How to Test

  1. Run ESLint across autocomplete family: npx eslint src/ui-kit/form-controls/{autocomplete,autocomplete-multiselect,sam-sds-autocomplete} (0 warnings, 0 errors)
  2. Run baseline check: npm run lint:baseline (44 warnings, passes gate)
  3. Run unit tests: npx vitest run --coverage (176 test files, 1,936 passing)
  4. Check coverage: npm run coverage:check (passes all metrics)
  5. Verify test app AOT build: npm --prefix test-app run build

Expected result: All lint checks, unit tests, coverage gates, and test-app AOT compilation pass.

Screenshots (if appropriate)

N/A — TypeScript type narrowing and lint cleanup only.

Checklist

  • Branch name follows convention (e.g. gh-<number>-<slug>)
  • PR title starts with a verb in the imperative mood
  • I have self-reviewed my own code
  • format:check passes (npm run format:check)
  • lint passes (npm run lint)
  • build passes (cd test-app && npm run build)
  • Tests pass and coverage is reported (cd test-app && npm test)
  • If this change requires a documentation update, I have updated it accordingly

Replaces `any`/legacy-typed provider consts/`Function`-shaped callbacks
across the autocomplete component family in src/ui-kit/form-controls,
following the same approach used for the non-autocomplete slice (#732)
and the parent issue #586.

- autocomplete/autocomplete.component.ts: introduced AutocompleteItem
  (Record<string, unknown>) for key/value option objects; typed
  options/categories/enterEvent/httpRequest/cache/keyEvents/
  propogateChange/onKeydown/setSelected/writeValue/registerOnChange
  against string | AutocompleteItem instead of any; NG_VALUE_ACCESSOR
  provider const typed as Angular's Provider
- autocomplete/autocomplete.service.ts: setFetchMethod/fetch return
  types narrowed from any to void/unknown[]
- autocomplete-multiselect/autocomplete-cache.ts: Cached/AutocompleteCache
  made generic (<T = unknown>) instead of any[]/any-typed members
- autocomplete-multiselect/autocomplete-multiselect.component.ts:
  introduced MultiselectItem and CategorizedList<T> (typed replacement
  for the associative-array-like `list` structure sortByCategory/
  filterOptions build); typed options/categories/serviceOptions/
  itemTemplate/writeValue/registerOnChange and the NodeList-based
  selection helpers
- sam-sds-autocomplete/{autocomplete,autocomplete-search,selected-result}:
  NG_VALUE_ACCESSOR provider consts typed as Provider;
  writeValue/registerOnChange/registerOnTouched/propogateChange
  narrowed to unknown/typed callbacks; TemplateRef<any> ->
  TemplateRef<unknown>; getFlatElements/checkItemSelected typed
- sds-selected-item-model-helper.ts: clearItems(model: any) ->
  clearItems(model: SAMSDSSelectedItemModel)
- autocomplete-seach-test-service.spec.ts: introduced
  HierarchicalDataItem interface for the sample fixture shape, replacing
  implicit any on loadedData/itemsListOutofObservable
- autocomplete-search.component.spec.ts: removed any from local test
  fixture types (propagated/previous/item locals)
- formly/components/autocomplete/test.service.ts: TestAutocompleteService
  updated to match AutocompleteService's Observable<unknown[]> contract

Lowers the root ESLint warning baseline 143 -> 44 (0 errors). All
previously-passing specs still pass (1936/1936); test-app build and
coverage-floor gate both pass with no regression.
@fpigeonjr fpigeonjr added the tech-debt Technical debt cleanup work label Sep 25, 2026
@fpigeonjr
fpigeonjr requested a lite review from Copilot September 25, 2026 17:36
@fpigeonjr
fpigeonjr marked this pull request as ready for review September 25, 2026 17:37
@fpigeonjr
fpigeonjr requested a review from a team as a code owner September 25, 2026 17:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Critical public API compatibility and test-compilation issues remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 9 High severity

Open (9)
What changed in this PR

This PR replaces unsafe TypeScript types across the autocomplete component family and reduces the ESLint warning baseline.

Changes:

  • Adds typed autocomplete items, caches, callbacks, services, and CVA APIs.
  • Updates related test fixtures and Formly typings.
  • Lowers the ESLint baseline from 143 to 44 warnings.
File Summary
src/​ui-kit/​form-controls/​sam-sds-autocomplete/​selected-result/​selected-result.component.ts Types selected-result CVA APIs.
src/​ui-kit/​form-controls/​sam-sds-autocomplete/​selected-result/​models/​sds-selected-item-model-helper.ts Types selected-item model clearing.
src/​ui-kit/​form-controls/​sam-sds-autocomplete/​autocomplete/​autocomplete.component.ts Types autocomplete CVA APIs.
src/​ui-kit/​form-controls/​sam-sds-autocomplete/​autocomplete-search/​autocomplete-search.component.ts Types search callbacks and helpers.
src/​ui-kit/​form-controls/​sam-sds-autocomplete/​autocomplete-search/​autocomplete-search.component.spec.ts Updates search test fixture types.
src/​ui-kit/​form-controls/​sam-sds-autocomplete/​autocomplete-search/​autocomplete-seach-test-service.spec.ts Adds typed hierarchical fixtures.
src/​ui-kit/​form-controls/​autocomplete/​autocomplete.service.ts Narrows service method types.
src/​ui-kit/​form-controls/​autocomplete/​autocomplete.component.ts Types autocomplete inputs and callbacks.
src/​ui-kit/​form-controls/​autocomplete-multiselect/​autocomplete-multiselect.component.ts Adds typed multiselect items and helpers.
src/​ui-kit/​form-controls/​autocomplete-multiselect/​autocomplete-cache.ts Makes cache implementations generic.
src/​formly/​components/​autocomplete/​test.service.ts Aligns the test service contract.
eslint-baseline.json Ratchets down the warning baseline.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/ui-kit/form-controls/autocomplete/autocomplete.component.ts Outdated
Comment thread src/ui-kit/form-controls/autocomplete/autocomplete.component.ts Outdated
Comment thread src/ui-kit/form-controls/autocomplete/autocomplete.service.ts Outdated
- MultiselectItem/AutocompleteItem: use `object` instead of
  `Record<string, unknown>` so consumers' named option interfaces
  (which lack a string index signature) remain assignable, matching
  the old `any[]` API's compatibility.
- CategorizedList.totalItems(): make optional so structural inputs
  (e.g. `{ 0: [], categories: [...] }`) built by callers/tests without
  the bookkeeping method still type-check.
- SamAutocompleteComponent.categories: keep `Array<string |
  AutocompleteItem>` instead of narrowing to `AutocompleteItem[]`, so
  plain string category labels are still accepted.
- AutocompleteService: make generic (`AutocompleteService<T =
  unknown>`) so `fetch()` returns `Observable<T[]>`; the standalone
  autocomplete component parameterizes it with `string |
  AutocompleteItem`, and existing untyped consumers keep compiling via
  the `unknown` default.
- Updated registerOnChange/registerOnTouched spec stubs in
  autocomplete-search, sam-sds-autocomplete, and selected-result specs
  to pass no-op functions instead of `{}`, matching the CVA callback
  signatures.
@rkflexion

Copy link
Copy Markdown

New problem found: two spec files were never updated for the signature changes

While verifying, ran a full tsc --noEmit and found the branch does not compile cleanly. Two component spec files — autocomplete.spec.ts and autocomplete-multiselect.spec.ts — were never updated when the component/service signatures changed in 4b49c454, unlike the three sam-sds-autocomplete specs and autocomplete-search.spec.ts which were fixed in 31cb9d3e. Copilot's lite review missed this because it only reviews diff hunks, not whole-file/whole-repo compile impact.

Confirmed via baseline diff (f9cfa863, pre-PR, vs current HEAD) that these are genuinely introduced by this PR, not pre-existing:

Pre-existing, NOT caused by this PR (already broken at baseline — ignore):

  • autocomplete.spec.ts: SamFormService stub mismatch (225,7), string→{name} conversion (591,15), 3× {} not assignable to Event (653, 1180, 1192)
  • autocomplete-multiselect.spec.ts: 15× Property 'list'/'ref'/'service' is private (unrelated private-member-access issue)

Newly introduced by this PR (need fixing):

autocomplete.spec.ts — 33 new errors:

  • 19× 'target' does not exist in type 'KeyEventLike' (lines 809, 817, 826, 834, 846, 856, 876, 878, 881, 886, 897, 904, 921, 936, 1208, 1255, 1263, 1278, 1283) — caused by narrowing onKeydown(event: any) → onKeydown(event: KeyEventLike); KeyEventLike has no target field but tests still pass { key, code, target: {...} }
  • 6× (string|object)[] not assignable to string[]/object[] (571, 578, 587, 589, 599, 601) — from narrowing categories/filterResults args
  • 3× AutocompleteService<unknown> not assignable to AutocompleteService<string|object> (950, 977, 1006) — from making AutocompleteService generic; test does new AutocompleteService() (defaults to <unknown>) then assigns to component.autocompleteService: AutocompleteService<string | AutocompleteItem>
  • 3× Subject<unknown> not assignable to Observable<(string|object)[]> (1029, 1041, 1057) — from typing httpRequest

autocomplete-multiselect.spec.ts — 6 new errors:

  • (99,25), (831,69): Property 'value' does not exist on type 'object' — from MultiselectItem = object
  • (278,33), (881,33), (1068,33): Property 'type' does not exist on type 'object' — same cause
  • (545,7): {0: [...]} not assignable to CategorizedList<object> — plain fixture object missing categories field

Recommendation

Fix these two spec files the same way ec00ed28 fixed the components — narrow local casts, no behavior change, no rework of the underlying types:

  • autocomplete.spec.ts:
    • Type service instances as new AutocompleteService<string | AutocompleteItem>() (3 call sites)
    • Type the Subject fixtures as Subject<Array<string | object>> instead of Subject<unknown> (3 call sites)
    • Either add an optional target?: { value?: string } field to KeyEventLike (in key-helper.ts) or drop the unused target property from the ~19 onKeydown({...}) test calls, since the component doesn't read it
    • Cast the category-array fixtures/assertions to match Array<string | AutocompleteItem> where string[]/object[] mismatches occur (571, 578, 587, 589, 599, 601)
  • autocomplete-multiselect.spec.ts:
    • Cast dynamic property reads the same way the component itself does: (test[0][0] as { value?: string }).value, (component.value[0] as { type?: string }).type (5 call sites)
    • Add a categories: [] (or similar) field to the {0: nested} fixture at line 545 so it satisfies CategorizedList<object>

Re-run npx tsc --noEmit -p tsconfig.json after each fix and confirm the only remaining non-clean output is the pre-existing baseline noise (private-member-access errors in the multiselect spec, the SamFormService/Event/string→{name} errors in the autocomplete spec, and the unrelated @angular/cdk/@angular/common/http/test-app config errors already present before this PR).

Update autocomplete.spec.ts and autocomplete-multiselect.spec.ts
to match component type signatures introduced in #728:
- Remove unused target property from onKeydown test calls in autocomplete.spec.ts
- Type AutocompleteService and Subject test fixtures with AutocompleteItem
- Cast options and category filter results to match narrowed parameter types
- Cast MultiselectItem dynamic property reads (.value, .type)
- Supply empty categories array on test fixture for CategorizedList
@fpigeonjr

Copy link
Copy Markdown
Contributor Author

Addressed in c2653a0.

Updated autocomplete.spec.ts and autocomplete-multiselect.spec.ts to align with the narrowed type signatures from #728:

  • autocomplete.spec.ts:
    • Removed unused target properties from the mock event objects passed to onKeydown (and focus handlers), keeping KeyEventLike tightly scoped without adding dead properties.
    • Parameterized AutocompleteService<string | AutocompleteItem> on all test instances.
    • Typed test Subject fixtures as Subject<Array<string | AutocompleteItem>>.
    • Added casts for filterResults and filterKeyValuePairs argument and return types.
  • autocomplete-multiselect.spec.ts:
    • Cast dynamic property reads (.value, .type) on MultiselectItem items matching established file patterns.
    • Provided empty categories: [] on the test fixture at line 545 to satisfy CategorizedList<object>.

Verification:

  • npx tsc --noEmit -p tsconfig.json: all 39 new errors cleared, leaving only pre-existing baseline findings.
  • npm run lint & npm run lint:baseline: 44 warnings, 0 errors (baseline gate passed).
  • npm run format:check: clean.
  • npm --prefix test-app test: 176 test files, 1,936 tests passed (100%).
  • npm run coverage:check: all metrics meet/exceed floors.
  • npm --prefix test-app run build: AOT production build succeeded.

@fpigeonjr fpigeonjr self-assigned this Sep 30, 2026
@fpigeonjr
fpigeonjr requested a review from rkflexion October 1, 2026 15:38
Comment thread src/ui-kit/form-controls/autocomplete/autocomplete.component.ts Outdated
Comment thread src/ui-kit/form-controls/autocomplete/autocomplete.component.ts Outdated
Comment thread src/ui-kit/form-controls/autocomplete/autocomplete.component.ts
@fpigeonjr
fpigeonjr merged commit 8d67d73 into master Oct 1, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tech-debt Technical debt cleanup work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace unsafe/legacy types in ui-kit/form-controls autocomplete family

3 participants