Index Signature — это механизм описания типа объектов, ключи которых заранее неизвестны (или могут динамически добавляться), а значения соответствуют определённому типу. Позволяет создавать объекты-словари (map/dictionary), где список свойств неизвестен на этапе компиляции.
История вопроса: в JavaScript объекты часто выступают как ассоциативные массивы. Статическая типизация TypeScript требует указать типы ключей и значений. Index Signature решает главную задачу: описать объект, где ключ — не жёстко прописанное свойство, а строка (или число), а значения — определенного типа.
Проблема: при работе с такими объектами можно попасть в ситуацию, когда известные свойства объекта конфликтуют с index signature, или теряется информация о конкретных свойствах. Есть нюансы с унаследованными типами и расширением структуры словаря.
Решение: Index Signature используется для описания динамических свойств, обычно с ключом типа string или number, например для коллекций, кэш-объектов или API-ответов вида { [key: string]: T }. Для сохранения контроля над типом необходимо описать и фиксированные свойства и index signature вместе, или, если требуется — составить union-типы.
Пример кода:
interface StringNumberMap { [key: string]: number; } const map: StringNumberMap = { apples: 2, oranges: 5, bananas: 3 };
Ключевые особенности:
Что произойдет, если добавить фиксированное свойство с несовместимым типом в интерфейс с index signature?
TypeScript выдаст ошибку, если тип свойства не совместим с типом, указанным в index signature.
interface BadDict { [key: string]: number; error: string; // Ошибка: string не совместим с number }
Можно ли объявить несколько index signature с разными типами ключей (number и string) одновременно?
Да, можно, но есть нюанс: в TypeScript ключи типа number автоматически приводятся к строке. Index signature с number — всего лишь синоним для string-ключей.
interface NumStrDict { [key: string]: number; [key: number]: number; }
Можно ли использовать типы union или интерфейсы для значения в index signature?
Да, значение может быть union-типом или интерфейсом. Таким образом можно описать словари сложных объектов.
interface Dict { [key: string]: string | number; }
В интерфейсе API-ответа использовали [key: string]: any. Позже возвращаемый объект содержал данные разных структур, что привело к ошибкам в рантайме и затруднило поддержку кода.
Плюсы:
Минусы:
Описали интерфейс кэш-объекта, где значения — строго определённого типа (например, UserData), с комментарием, что ключ — userId (string):
interface UserCache { [userId: string]: UserData; }
Плюсы:
Минусы: