Index Signature is a mechanism for describing the type of objects whose keys are unknown in advance (or can be dynamically added), and values correspond to a specific type. It allows for the creation of dictionary-like objects (map/dictionary) where the list of properties is not known at compile time.
Background: in JavaScript, objects often act as associative arrays. The static typing of TypeScript requires specifying the types of keys and values. The Index Signature addresses the main task: to describe an object where the key is not a rigidly defined property, but a string (or number), and the values are of a specific type.
Problem: when working with such objects, you can encounter situations where known properties of the object conflict with the index signature, or information about specific properties is lost. There are nuances with inherited types and expanding the structure of a dictionary.
Solution: The Index Signature is used to describe dynamic properties, usually with a key type of string or number, for example for collections, cache objects, or API responses of the form { [key: string]: T }. To maintain control over the type, it’s necessary to describe both fixed properties and the index signature together, or, if needed, to create union types.
Example code:
interface StringNumberMap { [key: string]: number; } const map: StringNumberMap = { apples: 2, oranges: 5, bananas: 3 };
Key features:
What happens if you add a fixed property with an incompatible type to an interface with an index signature?
TypeScript will raise an error if the property type is not compatible with the type specified in the index signature.
interface BadDict { [key: string]: number; error: string; // Error: string is not compatible with number }
Can multiple index signatures with different key types (number and string) be declared at the same time?
Yes, but there is a nuance: in TypeScript, keys of type number are automatically converted to string. An index signature with number is merely a synonym for string keys.
interface NumStrDict { [key: string]: number; [key: number]: number; }
Can union types or interfaces be used for the value in an index signature?
Yes, the value can be a union type or an interface. This allows for describing dictionaries of complex objects.
interface Dict { [key: string]: string | number; }
In the interface of an API response, [key: string]: any was used. Later, the returned object contained data of different structures, leading to runtime errors and complicating code maintenance.
Pros:
Cons:
An interface for a cache object was described, where values are of a strictly defined type (e.g., UserData), with a comment that the key is userId (string):
interface UserCache { [userId: string]: UserData; }
Pros:
Cons: