tezvyn:

TypeScript Index Signatures: Typing Dynamic Keys

AI-drafted, machine-checkedSource: typescriptlang.orgintermediate

An index signature is a blueprint for a dictionary, letting you type objects where you don't know property names ahead of time, but you know their values' type. Use it for configs or caches.

WHY IT EXISTS: JavaScript objects are often used as dictionaries or maps, with keys determined at runtime. Without index signatures, TypeScript can't validate the values of these dynamic properties, forcing you to use any and losing all type safety for that object.

THE MENTAL MODEL: Think of an index signature as a rule for a container, not a list of its contents. You're not defining explicit properties like name or id. Instead, you're defining a rule like [key: string]: boolean, which means "any property on this object that has a string for a key must have a boolean for a value."

HOW IT WORKS: You add [key: T]: U to an interface or type alias. The key type T can be string, number, or symbol. The value type U can be any TypeScript type. For example, interface StringDictionary { [key: string]: string; } defines a type for any object where all its values are strings, regardless of the key names.

WHEN TO USE IT: Use index signatures for dictionary-like structures where property names are not known at compile time. Three common places are: first, URL query parameters ([key: string]: string); second, feature flag objects ([key: string]: boolean); and third, simple data caches keyed by ID ([id: number]: UserProfile).

WHEN NOT TO USE IT: Avoid index signatures when you have a fixed, known set of properties. A standard interface with explicit properties is safer and more descriptive. Using an index signature there weakens type safety by allowing arbitrary extra properties. Also, if different keys need different value types, a mapped type or a union of objects is a better choice.

ONE CANONICAL EXAMPLE: A common use case is typing application settings that can be extended with arbitrary keys. Consider this interface: interface AppConfig { readonly port: number; [key: string]: string | number; }. Here, port is a known required property. The index signature [key: string]: string | number; allows any other string property, as long as its value is a string or a number. The key constraint is that the port property's type (number) must be assignable to the index signature's value type (string | number), which it is.

Read the original → typescriptlang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.