jsconfig.json option

noUncheckedIndexedAccess

  • Default false
  • Since TypeScript 4.1

Under strict alone, names[2] on a string[] is a string, even when the array has two elements, and a key looked up in a Record<string, number> is a number whether or not it exists. noUncheckedIndexedAccess adds undefined to those lookups, so each one needs a check, like any other value that may be missing.

It arrived with TypeScript 4.1 and is not part of strict. The strict level of the jsconfig generator turns it on, and so does the config that tsc --init writes in TypeScript 5.9.

At a glance

Write "noUncheckedIndexedAccess": true once strict passes; tsc --init in TypeScript 5.9 writes it
On the command line --noUncheckedIndexedAccess
Type boolean
Default in TypeScript 5.9 false
TypeScript's description Add 'undefined' to a type when accessed using an index.
Generator, relaxed not written
Generator, recommended not written
Generator, strict true

The description is quoted from TypeScript's own option list (Apache-2.0); see the notices. The generator rows are what the jsconfig generator writes at each level.

Examples

main.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: main.js(4,13): error TS18048: 'third' is possibly 'undefined'. main.js(9,13): error TS18048: 'stock.cake' is possibly 'undefined'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. main.js(4,13): error TS18048: 'third' is possibly 'undefined'. main.js(9,13): error TS18048: 'stock.cake' is possibly 'undefined'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. main.js(4,13): error TS18048: 'third' is possibly 'undefined'. main.js(9,13): error TS18048: 'stock.cake' is possibly 'undefined'. checked 21 October 2026
const names = ["Ada", "Grace"];
const third = names[2];
console.log(third.toUpperCase());error TS18048: 'third' is possibly 'undefined'.

/** @type {Record<string, number>} */
const stock = { tea: 12 };
console.log(stock.cake.toFixed(0));error TS18048: 'stock.cake' is possibly 'undefined'.

for (const name of names) console.log(name.toUpperCase());
noUncheckedIndexedAccess on: an index may point past the end.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": [],
    "noUncheckedIndexedAccess": true
  },
  "include": [
    "*.js"
  ]
}
main.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
const names = ["Ada", "Grace"];
const third = names[2];
console.log(third.toUpperCase());

/** @type {Record<string, number>} */
const stock = { tea: 12 };
console.log(stock.cake.toFixed(0));

for (const name of names) console.log(name.toUpperCase());
noUncheckedIndexedAccess off (strict alone): both lookups are trusted.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

What changes and what does not

Lookups by index or by key gain undefined; iteration does not:

  • array[i], and destructuring such as const [first] = array
  • record[key] and record.key for keys that come from an index signature
  • for (const item of array), array.forEach and array.map are unchanged, because they only visit elements that exist
  • tuples keep their exact types: the second element of a [string, number] is a number

Gotchas

  • Loops written with an index, for (let i = 0; i < a.length; i++) a[i]…, report every a[i] even though i is in range. A for…of loop reads better and needs no check.

Sources

Last reviewed 20 October 2026.