TypeScript error

TS2538: undefined used as an object key

  • Error TS2538
  • Reported with strictNullChecks
  • Reported with strict
  • Tested with TypeScript 5.9

error TS2538: Type 'undefined' cannot be used as an index type. is reported for object[key] when the key's type includes something that cannot be a property key. In JavaScript projects that something is nearly always undefined, from an optional property used as the key.

At run time the code would quietly use the key "undefined": totals["undefined"] collects every item without a category. The checker reports it instead.

At a glance

CodeTS2538
MessageType '{0}' cannot be used as an index type.
TypeScript's categoryerror
Listed underNull, undefined and unknown
Turned on by strictNullChecks, strict
In .js files Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file

The message is TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} is filled in by tsc for each error.

What it looks like

totals.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: totals.js(9,12): error TS2538: Type 'undefined' cannot be used as an index type. totals.js(9,37): error TS2538: Type 'undefined' cannot be used as an index type. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. totals.js(9,12): error TS2538: Type 'undefined' cannot be used as an index type. totals.js(9,37): error TS2538: Type 'undefined' cannot be used as an index type. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. totals.js(9,12): error TS2538: Type 'undefined' cannot be used as an index type. totals.js(9,37): error TS2538: Type 'undefined' cannot be used as an index type. checked 21 October 2026
/** @typedef {{ name: string, price: number, category?: string }} LineItem */

/** @param {LineItem[]} items */
export function totalsByCategory(items) {
  /** @type {Record<string, number>} */
  const totals = {};
  for (const item of items) {
    totals[item.category] = (totals[item.category] ?? 0) + item.price;error TS2538: Type 'undefined' cannot be used as an index type.error TS2538: Type 'undefined' cannot be used as an index type.
  }
  return totals;
}
An optional property used as an object key.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

What causes it

  • The key comes from an optional property or parameter (item.category where category?: string).
  • The key's type is an object or another type that is not string, number or symbol.

How to fix it

Choose a fallback key, or skip the item

const key = item.category ?? "other" gives every item a real key; if (!item.category) continue; leaves uncategorized items out. Either way the decision is in the code instead of in an accidental "undefined" entry.

totals.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
/** @typedef {{ name: string, price: number, category?: string }} LineItem */

/** @param {LineItem[]} items */
export function totalsByCategory(items) {
  /** @type {Record<string, number>} */
  const totals = {};
  for (const item of items) {
    const key = item.category ?? "other";
    totals[key] = (totals[key] ?? 0) + item.price;
  }
  return totals;
}
A fallback key for items without a category.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • Without noUncheckedIndexedAccess, reading totals[key] from a Record<string, number> is typed number even for a key that was never set, which is why the example writes ?? 0 itself.
  • Under noImplicitAny, a string key on an object without an index signature is TS7053, a different error.

Sources

Last reviewed 20 October 2026.