Объявление и инициализация словаря (Dictionary) в TypeScript
Основной способ: индексная сигнатура (Index Signature)
Самый распространённый и правильный способ объявить словарь в TypeScript — использовать индексную сигнатуру. Она говорит компилятору: "этот объект может иметь любое количество свойств с ключами определённого типа, и все значения будут одного типа".
// Пустой словарь
let dict: { [key: string]: number } = {};
// Добавление элементов
dict["apple"] = 5;
dict["banana"] = 10;
// Инициализация с данными
let dict2: { [key: string]: number } = {
"apple": 5,
"banana": 10,
"cherry": 15
};
// Попытка добавить значение другого типа вызовет ошибку
// dict["test"] = "hello"; // Ошибка: Type 'string' is not assignable to type 'number'
// Доступ к значению
console.log(dict["apple"]); // 5
Сокращённая запись: Record<K, V>
TypeScript предоставляет встроенный утилитарный тип Record, который делает то же самое, но короче:
let dict: Record<string, number> = {};
dict["apple"] = 5;
let dict2: Record<string, number> = {
"apple": 5,
"banana": 10
};
Record<K, V> — это просто синтаксический сахар для { [key: K]: V }. Используйте его для краткости.
Когда использовать Map вместо объекта?
В TypeScript (и JavaScript) есть встроенный класс Map, который тоже можно использовать как словарь. Вот основные отличия:
- Ключи: В объекте ключи могут быть только строками или символами. В Map ключом может быть любой тип (объект, число, функция).
- Порядок: Map гарантирует порядок вставки элементов. Объект (с ES6) тоже сохраняет порядок для строковых ключей, но это менее явно.
- Производительность: Map оптимизирован для частого добавления/удаления пар ключ-значение.
- Размер: У Map есть свойство
size. У объекта нужно использоватьObject.keys(obj).length.
// Map с типизацией
let map: Map<string, number> = new Map();
map.set("apple", 5);
map.set("banana", 10);
// Инициализация с данными
let map2: Map<string, number> = new Map([
["apple", 5],
["banana", 10]
]);
// Получение значения
console.log(map.get("apple")); // 5
// Проверка наличия ключа
console.log(map.has("apple")); // true
// Удаление
map.delete("apple");
Рекомендация
Используйте объект с индексной сигнатурой (Record), если:
- Ключи — только строки.
- Вам нужна простая структура данных без дополнительных методов.
- Вы работаете с JSON-данными (объекты легко сериализуются).
Используйте Map, если:
- Ключи — не строки (числа, объекты).
- Вам часто нужно проверять размер коллекции.
- Вы активно добавляете/удаляете элементы и важна производительность.
В вашем случае (ключи-строки, значения-числа) идеально подойдёт Record<string, number>.
Более строгая типизация с помощью интерфейса
Если вы знаете все возможные ключи словаря заранее, можно создать интерфейс с конкретными свойствами. Это даст ещё более строгую проверку — вы не сможете обратиться к несуществующему ключу без ошибки.
interface FruitPrices {
apple: number;
banana: number;
cherry?: number; // необязательное свойство
}
let dict: FruitPrices = {
apple: 5,
banana: 10
};
// Работает
dict["apple"] = 7;
dict.cherry = 15;
// Ошибка! Свойства 'orange' нет в интерфейсе
// dict["orange"] = 20; // Error
// Ошибка! Тип значения не соответствует
// dict["apple"] = "text"; // Error
Этот подход хорош, когда структура словаря фиксирована и известна на этапе разработки. Если ключи динамические (например, приходят из API), используйте Record или индексную сигнатуру.
Предостережение: пустой объект с as
Иногда новички делают так:
let dict = {} as Record<string, number>;
dict["apple"] = 5; // Работает
Это не рекомендуется, потому что as (приведение типа) обходит проверки компилятора. Вы можете случайно присвоить значение неправильного типа, и TypeScript не заметит ошибки до рантайма.
Лучше всегда использовать явную аннотацию типа:
let dict: Record<string, number> = {};
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги