TypeScript Index Signatures

Index signatures let you type objects whose exact property names aren't known ahead of time, such as dictionaries and dynamic lookup tables, while still keeping value types checked.

Basic Index Signatures

An index signature, written as [key: string]: ValueType inside an interface or type, tells TypeScript that any string key maps to a value of that type. It's the standard way to type dictionary-like objects.

A string-keyed dictionary

interface StringMap {
  [key: string]: string;
}

const colors: StringMap = {
  primary: "#0055ff",
  secondary: "#333333",
};

colors.accent = "#ff9900"; // OK, any string key allowed
console.log(colors["primary"]); // "#0055ff"

Numeric Index Signatures

A numeric index signature, [index: number]: ValueType, describes array-like structures. If an interface declares both a string and a number index signature, the numeric one's value type must be a subtype of the string one's.

Array-like access

interface FruitBasket {
  [index: number]: string;
}

const basket: FruitBasket = ["apple", "banana", "mango"];
console.log(basket[1]); // "banana"

Mixing Known Properties With an Index Signature

  • Named, fixed properties must be compatible with the index signature's value type
  • Combining Record<string, T> with specific keys via an intersection is a common escape hatch
  • A readonly index signature prevents reassigning any indexed value after creation
  • Every possible key is treated as accessible even if it doesn't exist at runtime, unless noUncheckedIndexedAccess is enabled

Fixed property plus dynamic settings

interface Config {
  name: string;          // must match the index signature's value type
  [key: string]: string; // extra dynamic settings
}

const config: Config = {
  name: "MyApp",
  theme: "dark",
  locale: "en-US",
};

// With noUncheckedIndexedAccess, this would type as string | undefined
const missing = config["doesNotExist"];
console.log(typeof missing); // "undefined" at runtime
ApproachType safety on readBest for
interface with index signaturemedium, no undefined by defaultmixing fixed + dynamic keys
Record<string, T>medium, concisepure dictionaries, no fixed keys
Map<K, V>high, .get returns V | undefinedfrequent inserts/deletes, iteration order
Note: Without noUncheckedIndexedAccess enabled in tsconfig.json, TypeScript assumes every indexed access succeeds, so config["typoedKey"] silently type-checks as string even though it's undefined at runtime.
Note: Reach for Record<string, T> for plain dictionaries; reach for an interface with an index signature when you need to mix a handful of guaranteed fixed properties with open-ended dynamic ones.

Exercise: TypeScript Index Signatures

What does the index signature `{ [key: string]: number }` allow?