Словари в TypeScript: объявление и инициализация с нуля

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Словари в TypeScript: от хаоса к порядку (полное руководство с кодом)

Представьте, что вы пишете приложение для изучения английского языка. Вам нужно хранить переводы слов: "cat" → "кошка", "dog" → "собака". Как лучше всего организовать эти данные? Конечно, использовать словарь (Dictionary) — структуру данных, где каждый ключ уникален и связан со значением.

В JavaScript словари реализуются через обычные объекты или Map. Но TypeScript добавляет мощную типизацию, превращая хаотичные коллекции в строгие, предсказуемые структуры. В этой статье мы разберём объявление и инициализацию Dictionary в TypeScript от простого к сложному. Вы узнаете, как избежать типичных ошибок и писать код, который работает предсказуемо.

Что такое Dictionary в TypeScript? (И почему это не просто объект)

В программировании Dictionary (словарь) — это коллекция пар «ключ-значение». В TypeScript вы можете создать его тремя основными способами:

  • Объект с сигнатурой индекса ({ [key: string]: T })
  • Record — утилитарный тип
  • Map — встроенный ES6 класс

Каждый подход имеет свои плюсы и нюансы. Давайте разберём их на реальных примерах.

Способ 1: Классический объект с сигнатурой индекса

Самый популярный способ объявить словарь в TypeScript — использовать сигнатуру индекса. Это говорит компилятору: «Объект может иметь любое количество свойств с ключами определённого типа».

// Объявляем тип словаря, где ключ — строка, значение — число
interface DictionaryNumber {
    [key: string]: number;
}

// Инициализация
const ages: DictionaryNumber = {
    "Анна": 25,
    "Пётр": 30,
    "Елена": 28
};

// Добавление нового элемента
ages["Ольга"] = 35;

console.log(ages["Анна"]); // 25

Важно: TypeScript требует, чтобы все значения соответствовали объявленному типу. Попытка добавить строку вызовет ошибку компиляции:

// Ошибка! Тип 'string' не может быть присвоен типу 'number'
ages["Ольга"] = "тридцать пять";

Когда использовать этот подход?

  • Когда нужен простой и быстрый словарь
  • Когда ключи — строки или числа
  • Для небольших коллекций (до 1000 элементов)

Способ 2: Record — элегантный синтаксис

TypeScript предоставляет встроенный утилитарный тип Record<K, V>. Он создаёт тип объекта, где все ключи имеют тип K, а значения — V.

// Объявление и инициализация в одной строке
const grades: Record<string, number> = {
    "Математика": 5,
    "Физика": 4,
    "История": 5
};

// Можно использовать enum для ключей
enum Subject {
    Math = "math",
    Physics = "physics",
    History = "history"
}

const gradesBySubject: Record<Subject, number> = {
    [Subject.Math]: 5,
    [Subject.Physics]: 4,
    [Subject.History]: 5
};

Преимущества Record:

  • Краткий синтаксис
  • Хорошая читаемость
  • Поддержка union-типов для ключей

Способ 3: Map — для продвинутых сценариев

Класс Map из ES6 — это полноценная коллекция «ключ-значение» с дополнительными возможностями. В TypeScript его типизируют через дженерики:

// Объявление Map
const userRoles: Map<string, string> = new Map([
    ["admin", "Администратор"],
    ["user", "Пользователь"],
    ["guest", "Гость"]
]);

// Добавление
userRoles.set("moderator", "Модератор");

// Получение
console.log(userRoles.get("admin")); // "Администратор"

// Проверка наличия
if (userRoles.has("user")) {
    console.log("Роль существует");
}

// Итерация
userRoles.forEach((value, key) => {
    console.log(`${key}: ${value}`);
});

Когда Map лучше объекта?

  • Когда ключи не только строки (например, объекты или символы)
  • Когда нужна гарантированная сохранность порядка вставки
  • Когда часто удаляете/добавляете элементы
  • Когда нужны методы .size, .clear(), .delete()

Как правильно инициализировать пустой словарь?

Часто возникает ситуация: нужно объявить словарь, но наполнить его данными позже. Вот правильные паттерны:

// Для объекта
const emptyDict: Record<string, number> = {};

// Для Map
const emptyMap = new Map<string, number>();

// Добавление позже
emptyDict["key1"] = 100;
emptyMap.set("key2", 200);

Ошибка новичка:

// Некорректно! TypeScript выдаст ошибку
const dict: Record<string, number> = {};
dict["test"] = "строка"; // Ошибка

Типизированные словари с пользовательскими типами

Словари могут хранить не только примитивы, но и сложные объекты:

interface Product {
    id: number;
    name: string;
    price: number;
}

// Словарь товаров по ID
const products: Record<number, Product> = {
    1: { id: 1, name: "Ноутбук", price: 50000 },
    2: { id: 2, name: "Мышь", price: 1500 }
};

// Добавление нового товара
products[3] = { id: 3, name: "Клавиатура", price: 3000 };

// Обход словаря
Object.values(products).forEach(product => {
    console.log(`${product.name}: ${product.price}₽`);
});

5 частых ошибок при работе со словарями в TypeScript

Даже опытные разработчики иногда попадаются на этих граблях:

  1. Забыть про undefined — при обращении к несуществующему ключу объекта вы получите undefined. Всегда проверяйте:
const dict: Record<string, number> = { "a": 1 };
const value = dict["b"]; // undefined, но тип number!
// Лучше: const value = dict["b"] ?? 0;
  1. Мутация через индексацию — TypeScript не всегда ловит опечатки в ключах. Используйте as const для строгих ключей:
const strictDict = { "apple": 5, "banana": 3 } as const                

Блоги

Рекомендаций по книгам