Index Signatures in TypeScript

An index signature lets you type objects when you do not know the exact property names ahead of time, only the pattern of keys and values. Learn the syntax and when to use it.

5 min read

TypeScript index signatures describe the type of values you get when you access an object with an unknown key. You use one when you do not know the exact property names ahead of time, but you do know the pattern: every property key is some type, and every property value is some type.

This is common for dictionary-like objects, configuration maps, and any object where keys are dynamic.

String index signatures

The most common index signature uses string keys. It says: for any string key, the value at that key is of a given type.

typescripttypescript
interface StringMap {
  [key: string]: string;
}
 
const translations: StringMap = {
  hello: "Hola",
  goodbye: "Adios",
  thank_you: "Gracias",
};
 
console.log(translations["hello"]); // "Hola"

The syntax [key: string]: string means that any string key on a StringMap object returns a string value. The key name -- here called key -- is arbitrary. You could call it index, name, or anything else. It is a placeholder that never appears in your code.

String index signatures also enforce that every explicitly named property matches the index type. If you declare a name property of type string alongside a string index of type number, the compiler reports an error:

typescripttypescript
interface BadDict {
  [key: string]: number;
  length: number;
  name: string;
  // Error: 'name' must be number to match index signature
}

To allow mixed types across different named properties, widen the index value type to a union. This lets length be a number while name is a string, because the index signature accepts both:

typescripttypescript
interface MixedDict {
  [key: string]: number | string;
  length: number;
  name: string;
}

Number index signatures

A number index signature describes array-like access. When you access the object with a number, you get back a value of the given type.

typescripttypescript
interface NumberArray { [index: number]: string; }
 
const arr: NumberArray = ["a", "b", "c"];
console.log(arr[1]); // "b"

There is an important rule: the type returned by a number indexer must be a subtype of the type returned by any string indexer on the same interface. This is because JavaScript converts numeric keys to strings when indexing objects. Accessing arr[1] is the same as arr["1"] at runtime.

Template string index signatures

TypeScript also supports index signatures with template string patterns. Use these when keys follow a known prefix.

typescripttypescript
interface Headers {
  "content-type": string;
  "content-length": string;
  [key: `x-${string}`]: string;
}

The pattern x-${string} matches any key starting with "x-". This is useful for HTTP custom headers. Here is how you would read those headers:

typescripttypescript
function handle(r: Headers) {
  console.log(r["content-type"]);
  console.log(r["x-powered-by"]); // OK
}

An unknown key without the prefix produces an error. This gives you precise control over which dynamic keys are allowed while keeping standard properties fully typed.

Readonly index signatures

Add the readonly modifier to prevent assignment through bracket notation.

typescripttypescript
interface ReadonlyMap {
  readonly [key: string]: number;
}
 
const scores: ReadonlyMap = { alice: 95, bob: 87 };
scores["alice"] = 100;
// Error: Index signature only permits reading.

This makes the entire index immutable at compile time. You can still read any value, but you cannot set new ones or change existing ones through the index.

When to use index signatures

Use an index signature when you need to type an object with unknown or dynamic keys -- dictionaries, lookup tables, configuration maps, or API response objects where the keys vary. The index signature tells TypeScript what type of value to expect for any key, without listing every possible key.

Do not use an index signature when you know the full set of property names. In that case, declare each property explicitly. The explicit declaration gives you better autocomplete, more precise type checking, and clearer error messages.

For more on object types, see object types in TypeScript and interfaces in TypeScript explained.

Common mistakes

Forgetting that named properties must match the index type. If your index signature returns number, every named property must also be number (or a subtype). Use a union in the index signature if you need different types for different named properties.

Using an index signature when you know the keys. If you know every property name, declare them explicitly. Index signatures sacrifice precision for flexibility. Use them only when you need that flexibility.

Rune AI

Rune AI

Key Insights

  • An index signature describes the type of values for any key matching a pattern.
  • String index signatures are the most common, used for dictionary and config objects.
  • Number indexers must return a subtype of the string indexer's type.
  • Named properties must be compatible with the index signature's value type.
  • Use readonly index signatures to prevent assignment through bracket notation.
RunePowered by Rune AI

Frequently Asked Questions

Can I have both a string and a number index signature?

Yes, but the type returned from the number indexer must be a subtype of the type returned from the string indexer. This is because JavaScript converts numeric keys to strings when indexing objects.

Can index signatures be readonly?

Yes. Add readonly before the index signature: `readonly [key: string]: string`. This prevents assignment to any indexed property without explicitly naming it.

How do index signatures interact with explicitly named properties?

Named properties must be assignable to the index signature's value type. If you have `[key: string]: number`, every named property must also be a number (or a subtype). Use a union in the index signature if you need mixed types.

Conclusion

Index signatures type objects when you know the value pattern but not the exact keys. Use a string index for dictionary-like objects, a number index for array-like access, and template string indexes for prefix or suffix patterns. Remember that named properties must match the index signature's value type.