Bug-Catching TS Flags: noUncheckedIndexedAccess & exactOptionalPropertyTypes

Two TypeScript options that catch real production bugs: safer indexing and optional properties that behave like you think.

F

Frontend Interview Team

March 01, 2026

2 min read
Bug-Catching TS Flags: noUncheckedIndexedAccess & exactOptionalPropertyTypes

What you’ll learn

  • Why obj[key] is usually unsafe
  • What noUncheckedIndexedAccess changes and the trade-offs
  • Why exactOptionalPropertyTypes prevents subtle API bugs

30‑second interview answer

noUncheckedIndexedAccess makes indexed access return T | undefined because the key might not exist. This catches real crashes in maps and object lookups. exactOptionalPropertyTypes makes optional properties truly “possibly missing”, not “present but undefined”, which improves correctness for APIs and patch objects. Both flags increase safety but may require stricter checks.


1) noUncheckedIndexedAccess

The bug

const scores: Record<string, number> = {
  alice: 10
};
 
function doubleScore(name: string) {
  return scores[name] * 2;
}
 
// name = "bob" → scores["bob"] is undefined → NaN

Without the flag, TS assumes scores[name] is number.

With noUncheckedIndexedAccess

scores[name] becomes number | undefined, forcing you to handle the missing case:

function doubleScore(name: string) {
  const score = scores[name];
  if (score === undefined) return 0;
  return score * 2;
}

Trade-off

  • More undefined handling
  • But fewer runtime surprises (especially in large apps)

2) exactOptionalPropertyTypes

The confusion

Optional property x?: string is not always the same as x: string | undefined.

In real APIs, “missing” often means “don’t change it”, while undefined might mean “clear it”.

Example: patch object

type PatchUser = {
  name?: string;
};
 
function applyPatch(patch: PatchUser) {
  // should treat missing as "no change"
}

With exactOptionalPropertyTypes, TS becomes stricter:

  • You can omit name
  • But you can’t freely assign name: undefined unless your type explicitly allows it

This prevents accidentally sending { name: undefined } over the wire.

Fix when you actually want undefined

Be explicit:

type PatchUser = {
  name?: string | undefined;
};

When to enable these flags

Enable when:

  • You have lots of map/dictionary lookups
  • You build patch/update APIs
  • You want TS to behave closer to runtime truth

Be careful when:

  • You’re migrating a huge legacy codebase (do it incrementally)

Production rule of thumb

  • noUncheckedIndexedAccess is worth it for correctness in serious apps.
  • exactOptionalPropertyTypes is excellent for API clients and patch semantics.

Interview questions

  1. Q: Why does noUncheckedIndexedAccess exist?

    • A: Because indexing can fail at runtime; the type should reflect that possibility.
  2. Q: Why is optional not the same as | undefined?

    • A: Optional means “may be missing entirely”, which is important for API semantics.

Quick recap

  • Indexing is unsafe by default; the flag forces you to handle it.
  • Optional properties have subtle meaning; exact optional types make it explicit.
  • Both flags catch real bugs.