C.W.K.
Stream
Lesson 04 of 05 · published

`noUncheckedIndexedAccess`

~7 min · strict-mode, no-unchecked-indexed-access, arrays

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"arr[5] might not exist. The type system should say so."

What it adds

Without noUncheckedIndexedAccess, an array index access has the type of the array's element. arr: string[] means arr[0] is string. But arr[100] is also typed string — even though at runtime it's undefined. The type system lies for indices that don't exist.

With the flag, every index access has type T | undefined. arr[0] on a string[] is string | undefined. You must handle the undefined case before using the value. The type system finally matches runtime reality.

Why it's separate from strict

The flag isn't part of strict: true because retrofitting it to an existing codebase requires touching every array index access. For new code, turn it on. For legacy code, plan the migration carefully.

Object index signatures too

The flag also affects index-signature object access. const obj: Record<string, User> = {}; obj['anyKey'] is User | undefined with the flag on. This is correct — a Record can return undefined for missing keys.

noUncheckedIndexedAccess is the most honest flag in TypeScript. It refuses to pretend index access can't return undefined. The discipline it forces — always narrow before use — eliminates a whole category of 'undefined is not an object' runtime bugs.

Code

Index access — with and without the flag·typescript
// Without noUncheckedIndexedAccess — silent lie.
const arr: string[] = ['a', 'b'];
const x = arr[100];        // x: string — but actually undefined
x.toUpperCase();           // crashes at runtime

// With the flag:
const arr2: string[] = ['a', 'b'];
const y = arr2[100];       // y: string | undefined
// y.toUpperCase();         // ❌ Object is possibly 'undefined'
if (y !== undefined) {
  y.toUpperCase();         // ✅ narrowed to string
}

// Object index signatures also get T | undefined.
const map: Record<string, User> = { pippa: {} as User };
const u = map['pippa'];    // u: User | undefined
const u2 = map['missing']; // u2: User | undefined — accurately

// Optional chaining is your friend.
map['pippa']?.name;        // string | undefined

External links

Exercise

In a small project, declare an array of 3 strings, access arr[10], and use the result. With the flag off — compiles, runtime undefined. With the flag on — compiler refuses until you handle undefined. How many places in your existing project would this flag light up?
Hint
Probably more than you'd guess. Every index access is suspect. The discipline of always-narrow-after-index becomes second nature quickly.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.