| name | noUncheckedIndexedAccess |
| description | Conservative, low-risk fixes for TypeScript errors introduced by enabling noUncheckedIndexedAccess, with different policies for test vs production code and a bias toward non-cascading changes. |
noUncheckedIndexedAccess Violation Resolver
Enable noUncheckedIndexedAccess while keeping changes safe, local, and reviewable.
noUncheckedIndexedAccess changes indexed access from T to T | undefined (arrays, objects with index signatures, Record<...>, etc.). This skill focuses on fixing the resulting type errors without turning the migration into a signature-changing domino chain.
When to Use
Use this skill when you see TypeScript errors that appear after enabling noUncheckedIndexedAccess, commonly:
- TS2532: Object is possibly 'undefined'
- TS18048: 'x' is possibly 'undefined'
- TS2345: Type 'T | undefined' is not assignable to type 'T'
- TS2538: Type 'undefined' cannot be used as an index type
Mode Detection: Tests vs Production
Test files (lower risk):
- Paths matching
**/*.test.*,**/*.spec.*,**/__tests__/**,**/test/** - Or files that clearly use test frameworks (
jest,vitest) / Testing Library (@testing-library/*)
Production code (higher risk):
- Everything else, especially shared libraries and app runtime code
Core Principles
Local fixes beat cascading API changes
Prefer local guards, defaults, and boundary validation over changing return types or parameter types.Production code: avoid unsafe assertions
Do not useas any,as unknown,as never, or blanket@ts-ignoreas a quick escape hatch.Tests have different ergonomics
In tests, it’s acceptable to use:- non-null assertions (
!) for ergonomics, especially after runtime expectations - test-only assertion helpers that narrow types (preferred over
!for “type narrowing”)
- non-null assertions (
Suppressions must be deliberate
- Prefer
@ts-expect-errorover@ts-ignore(because it self-invalidates once fixed) - In production code,
@ts-expect-errormust include a ticket + expiry date (or equivalent)
- Prefer
Fix Policy
Test Code Policy (preferred order)
Use test helpers that narrow types
Seereferences/test-helpers.mdforexpectString,expectDefined,at(), etc.Use
!for indexing after an explicit runtime assertion
Example:expect(rows).toHaveLength(2)thenrows[0]!.Use
@ts-expect-erroronly for intentionally invalid test cases
Avoid it for normal indexing/narrowing.Avoid
@ts-ignore
It disables type checking too broadly.
Production Code Policy (preferred order)
- Defaulting (
??) when a sensible fallback exists - Guard/early return when undefined is acceptable and local handling is correct
- Throw with a clear error when undefined is a programmer/configuration error
- Tighten types (e.g., restrict key unions,
as constobjects) when keys are known - Non-null assertion (
!) only for true invariants (and comment if non-obvious) @ts-expect-erroronly when the correct fix is genuinely structural and must be deferred (ticket + expiry required)
Avoiding the “Spiral”
Before changing a function signature (e.g., returning T | undefined where it previously returned T), ask:
- Can we handle
undefinedlocally at the call site? - Is this value coming from a boundary (URL params, env/config, JSON parsing)? Validate once at the boundary.
- Would a signature change force many callers to change? If yes, prefer local handling or upstream validation.
Signature changes can be correct, but they must be deliberate and usually handled in a focused PR.
Recommended Lint Guardrails
To prevent “! creep” in production code:
- Enable
@typescript-eslint/no-non-null-assertionin production code (error or warn) - Enable
@typescript-eslint/no-unnecessary-type-assertion(type-aware) to catch some unnecessary assertions
Tests can override no-non-null-assertion to allow ergonomic !.
Note:
no-unnecessary-type-assertionrequires type-aware ESLint (parserOptions.project, often viatsconfig.eslint.json).
Workflow
- Confirm scope: test vs production
- Classify the violation:
- array indexing
- object index signature /
Record - map/dictionary lookup
- boundary data (config/env/user input)
- falsy vs undefined confusion
- Apply the lowest-risk fix using the policies above
- Run typecheck/tests (or describe what should be run in the repo)
- Record the fix in
logs/{project-name}-resolution-log.mdwhere{project-name}is derived from the current working directory basename (e.g.,cops-portal,seller-portal,fe-core). Record what changed and why – UNLESS it is a trivial application of a non-null assertion in a test file, we do not need to log these. If we have already created a code comment documenting our justifications, these log entries ought to be as concise as possible. Simply append to this log file, do NOT read the whole thing or it will fill up the context window quickly
References
references/fix-patterns.md— common motifs and the recommended fix for eachreferences/test-helpers.md— assertion helpers that narrow types in testsreferences/known-issues.md— edge cases / novel patterns worth remembering
Success Criteria
- Errors resolved with minimal runtime behavior changes
- Production code avoids unsafe type assertions and broad suppressions
- Test code fixes are ergonomic and maintainable
- Fixes are logged for review and repeatability