TypeScript Map: как типизировать ключи и значения

Короткий ответ: typescript map может означать две разные вещи. Первая — метод массива Array.map, который преобразует каждый элемент. Вторая — структура данных Map<K, V>, где есть ключи и значения. Их важно не путать

Array.map

type Product = {
  id: number;
  title: string;
  price: number;
};

const products: Product[] = [
  { id: 1, title: "Книга", price: 900 },
  { id: 2, title: "Курс", price: 4900 },
];

const titles = products.map((product) => product.title);

titles будет string[]. TypeScript сам выводит тип результата

Явный тип результата

type ProductView = {
  label: string;
  value: number;
};

const views: ProductView[] = products.map((product) => ({
  label: product.title,
  value: product.price,
}));

Если забыть value, TypeScript покажет ошибку, потому что результат должен соответствовать ProductView

Map<K, V>

Структура Map типизируется так:

const usersById = new Map<number, string>();

usersById.set(1, "Dinar");
usersById.set(2, "Alya");

Здесь ключ — number, значение — string

Получение:

const name = usersById.get(1);

Тип name будет string | undefined, потому что ключа может не быть

Проверка undefined

const name = usersById.get(3);

if (name !== undefined) {
  console.log(name.toUpperCase());
}

Не используйте name! без необходимости. Оператор ! просто заставляет TypeScript поверить вам, но не проверяет данные

Async map

Частая ошибка:

const results = users.map(async (user) => loadProfile(user.id));

results будет Promise<Profile>[], а не Profile[]

Правильно:

const results = await Promise.all(
  users.map((user) => loadProfile(user.id))
);

map с объектом-словарем

Если данные лежат в обычном объекте, у него нет метода .map:

const prices = {
  book: 900,
  course: 4900,
};

Чтобы преобразовать его, используйте Object.entries:

const items = Object.entries(prices).map(([key, price]) => ({
  key,
  price,
}));

Если нужен строгий набор ключей, задайте тип:

type PriceKey = "book" | "course";
const prices: Record<PriceKey, number> = {
  book: 900,
  course: 4900,
};

Так TypeScript проверит, что вы не забыли нужный ключ и не добавили случайный

Типизация callback

Обычно TypeScript сам понимает тип элемента:

products.map((product) => product.title);

Но если исходный массив не типизирован, callback тоже станет слабым. Поэтому начинайте с типа массива:

const products: Product[] = [];

Не нужно писать тип у каждого параметра map, если TypeScript уже выводит его из массива. Лучше типизировать источник данных, а не лечить каждый callback отдельно

Частые ошибки

Первая ошибка — путать Array.map и Map. Это разные вещи

Вторая ошибка — забывать, что Map.get() возвращает undefined, если ключа нет

Третья ошибка — не возвращать значение из callback:

const ids = products.map((product) => {
  product.id;
});

Так получится void[]. Нужно:

const ids = products.map((product) => product.id);

Четвертая ошибка — ожидать, что async map сразу даст готовые значения. Нужен Promise.all

Пятая ошибка — пытаться вызвать .map у объекта. Метод .map есть у массива, а для объекта используйте Object.keys, Object.values или Object.entries

Самопроверка

Создайте массив товаров и получите массив строковых названий через map. Потом создайте Map<number, Product>, получите элемент через get и обработайте случай undefined

Что почитать дальше по TypeScript

Если нужен общий маршрут по теме, откройте рубрику TypeScript. Для соседних задач пригодятся эти разборы:

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x