Индексные сигнатуры и вложенные структуры
Типизируем объекты-словари с заранее неизвестными ключами и сложные вложенные структуры.
Индексная сигнатура описывает объект, у которого ключи заранее неизвестны, но известен их тип и тип значений:
{ [key: string]: number }.
Проблема: ключи неизвестны заранее
interface с фиксированными свойствами не подходит, когда объект используется как словарь — например, «название города → население». Имена ключей появляются динамически. Для этого есть индексная сигнатура:
interface Population {
[city: string]: number;
}
const data: Population = {
"Москва": 13_000_000,
"Казань": 1_300_000,
};
data["Уфа"] = 1_100_000; // ок
data["Сочи"] = "много"; // Ошибка: значение должно быть number
Запись [city: string]: number читается как «любой строковый ключ ведёт к числу». Имя city здесь только для читаемости.
Где это применяют
Индексные сигнатуры описывают словари, кэши, счётчики, объекты-конфиги — всё, где ключи задаются данными, а не известны заранее. Реальный пример — подсчёт частоты слов:
const text = "кот кот пёс кот пёс";
const counts = {};
for (const word of text.split(" ")) {
counts[word] = (counts[word] || 0) + 1;
}
for (const word in counts) {
console.log(word + ": " + counts[word]);
}
Вывод:
кот: 3 пёс: 2
В TypeScript объект counts описали бы как { [word: string]: number } — и компилятор гарантировал бы, что значения всегда числа.
Сочетание с фиксированными свойствами
Можно совместить известные поля и индексную сигнатуру. Но тогда тип фиксированных свойств обязан быть совместим с типом значений сигнатуры:
interface Config {
version: string;
[key: string]: string; // все остальные ключи — тоже строки
}
Вложенные структуры
Реальные данные часто вложены: объект внутри объекта, массив объектов. Типы вкладываются так же, как сами данные:
interface Address {
city: string;
zip: string;
}
interface User {
name: string;
address: Address; // вложенный объект
hobbies: string[]; // массив строк
friends: User[]; // массив таких же объектов
}
const u: User = {
name: "Аня",
address: { city: "Казань", zip: "420000" },
hobbies: ["чтение", "бег"],
friends: [],
};
Разбивать сложную структуру на отдельные интерфейсы (Address, User) — хорошая практика: каждый кусок переиспользуется и читается понятнее, чем один гигантский вложенный тип.
Доступ к вложенным полям
При обращении к глубоким полям редактор подсказывает на каждом шаге, а компилятор ловит опечатки на любом уровне вложенности:
console.log(u.address.city); // "Казань"
console.log(u.address.country); // Ошибка: Property 'country' does not exist on type 'Address'.
Итог
- Индексная сигнатура
{ [k: string]: T }типизирует объекты-словари с динамическими ключами. - Её применяют для кэшей, счётчиков, конфигов — там, где ключи приходят из данных.
- Вложенные структуры описывают отдельными интерфейсами; компилятор проверяет поля на любой глубине.