Compiler answer
noUncheckedIndexedAccess makes TypeScript admit that items[0] or map[key] may be undefined.
Why array syntax promises too much
This option surprises teams that turn on stricter TypeScript settings.
What the option changes
The option is annoying because it is honest. JavaScript does not guarantee an array index or object key exists just because the type says the container exists.
| Access pattern | Risk |
|---|---|
| array[0] | Array may be empty |
| record[key] | Key may not exist |
| split result | Expected segment may be missing |
Fix a lookup without hiding undefined
const parts = "user:123".split(":");
const type = parts[0];
const id = parts[1];
if (!id) {
throw new Error("Missing id segment");
}
This example is intentionally small. In a real codebase, the surrounding details matter: naming, error handling, tests, runtime config, permissions, and how easy the next developer can understand the change.
Choose a response for each collection
- Enable it in libraries or high-risk backend code first.
- Add helper functions for required lookups.
- Use guards instead of non-null assertions everywhere.
- Expect more checks around arrays and records.
- Do not enable it during a rushed release.
Unsafe escapes that defeat the check
- Fixing every error with
!. - Turning it on without team agreement.
- Ignoring why the value can be missing.
- Using arrays where named objects are clearer.
- Treating the option as required for every project.
Explain the runtime possibility
Use a concrete sentence:
I used this pattern because [problem]. The main tradeoff was [tradeoff]. I verified it by [test or check].
That structure works because it shows judgment. Anyone can name a tool. Strong developers explain why they chose it, what could go wrong, and how they checked the result.
Related TypeScript guides
TypeScript references
Make absence explicit
noUncheckedIndexedAccess makes TypeScript admit that items[0] or map[key] may be undefined. Keep the implementation small, verify the edge cases, and write the decision down so the next person can trust it.
Fix the domain behavior, not only the type error
This code compiles without noUncheckedIndexedAccess even when no matching event exists:
const latest = eventsByAccount[accountId];
return latest.score;
With the option enabled, latest becomes FraudEvent | undefined. The correct fix depends on the domain:
const latest = eventsByAccount[accountId];
if (!latest) {
return { state: 'insufficient_data', signals: [] };
}
return { state: 'evaluated', signals: latest.signals };
In SentinelFi, “no event found” should not silently become a zero-risk decision. It is a missing-data state that policy and monitoring can distinguish. Writing eventsByAccount[accountId]! merely restores the original unchecked assumption.
Introduce the option with a bounded migration
Enable it in a branch, group errors by pattern, and fix shared utilities before leaf code. Arrays often need length checks, records may need explicit optional values, and environment maps may need startup validation. Avoid adding non-null assertions mechanically.
Run the complete type check and runtime tests because the compiler can reveal missing cases but cannot prove your fallback is correct. Update code-review guidance so future map and array access uses a deliberate absence policy instead of another assertion.
Questions for each new undefined value
Is absence expected, impossible, or evidence of corrupted state? Should the caller skip the item, return a not-found result, use a default, or stop the operation? Where can the condition be narrowed once so downstream code stays simple?
Do not answer every question with optional chaining. item?.value can turn a missing invariant into silent undefined data. The option is valuable because it forces a domain decision at the access point; preserve that pressure during migration.
Make absence visible deliberately.
Discussion
What would you try, change, or challenge after reading this guide? Specific results and errors help the next reader.
Comments will load as you reach this section.