Map в TypeScript это встроенный Map из JavaScript с типизированными ключами и значениями: Map<string, number> отображает строки в числа. Создайте его через new Map<K, V>(), а затем используйте set, get, has и delete. get возвращает V | undefined, потому что ключа может не быть.
Именно аргументы типа делают Map полезным в TypeScript: каждый set проверяется, а каждый get возвращает тип значения. Последняя строка это ошибка компиляции (TS2345); @ts-expect-error позволяет блоку выполниться.
Эта страница о коллекции Map. Если вы искали array.map(), он разобран в последнем разделе.
Создание Map
Типы берутся из аргументов типа, из начальных записей или из аннотации. Начальные записи это массив кортежей [key, value] или что угодно ещё, что их выдаёт.
Две ловушки вывода типов:
new Map()без аргументов типа и без записей этоMap<any, any>. Ничего из того, что вы кладёте и достаёте, не проверяется. Всегда пишитеnew Map<K, V>().- Записи с разными типами значений не выводят объединение.
new Map([["a", 1], ["b", "x"]])даёт ошибку TS2769 (No overload matches this call). Укажите тип:new Map<string, number | string>([...]).
get возвращает V | undefined
Map не может обещать, что ключ существует, поэтому get типизирован как V | undefined. В режиме strict нужно разобраться с undefined, прежде чем использовать значение как V:
index.ts(4,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
Способы исправить, от самого частого к самому редкому:
TypeScript не запоминает, что has вернул true, когда вы потом вызываете get. Проверять результат get напрямую и короче, и безопасно. Non-null утверждение, stock.get("apples")!, убирает ошибку, но не защищает, если ключа нет.
Методы Map и их типы
| Член | Тип для Map<K, V> | Примечания |
|---|---|---|
new Map<K, V>(entries?) | Map<K, V> | entries: итерируемый объект из [K, V] |
set(key, value) | this | Добавляет или заменяет; вызовы можно связывать в цепочку |
get(key) | V | undefined | undefined, если ключа нет |
has(key) | boolean | |
delete(key) | boolean | true, если что-то было удалено |
clear() | void | Удаляет всё |
size | number | Свойство, а не метод |
keys(), values() | итераторы по K, V | Разверните в массив: [...map.keys()] |
entries(), for...of | итератор по [K, V] | Порядок вставки |
forEach((value, key) => ...) | void | Обратите внимание: значение идёт первым |
Перебор Map
Map перебирается в порядке вставки. for...of по Map выдаёт кортежи [key, value] с типом [K, V].
Запись по существующему ключу обновляет значение, но сохраняет его исходную позицию в порядке.
Ключи-объекты и подсчёт
Ключом может быть любое значение, включая объекты и массивы. Ключи сравниваются как ===: два объекта с одинаковым содержимым это разные ключи. Map также стандартный способ подсчитать или сгруппировать элементы.
Чтобы использовать ключ по содержимому объекта, получите из него строковый или числовой ключ, например user.id или `${x},${y}`.
Map, объект или Record
Map<K, V> | Объект / Record<string, V> | |
|---|---|---|
| Типы ключей | любые, сравниваются как === | string (числа становятся строками), symbol |
| Тип при отсутствии ключа | get возвращает V | undefined | obj[key] имеет тип V, если не включена noUncheckedIndexedAccess |
| Порядок | порядок вставки | в основном порядок вставки, но целочисленные ключи идут первыми по возрастанию |
| Размер | map.size | Object.keys(obj).length |
| Частые добавления и удаления | оптимизирован для этого | не оптимизирован для этого |
| JSON | не напрямую (JSON.stringify(map) даёт "{}") | напрямую |
| Литеральный синтаксис, деструктуризация | нет | да |
| Случайные унаследованные ключи | нет | "toString" in {} равно true |
Простое правило: Map для коллекции, ключи которой являются данными (id пользователей, слова, записи кэша) и меняются во время выполнения; объектный тип или Record для фиксированного набора известных ключей и для всего, что идёт в JSON или из него. На странице словарь индексные сигнатуры, Record и Map сравниваются для поиска по строковым ключам.
Преобразование Map в объект и JSON
Записи Map не являются свойствами, поэтому JSON.stringify их не видит. Преобразуйте через Object.fromEntries и Object.entries:
JSON.parse возвращает any, поэтому as Record<...> указывает, какими данные должны быть. Это не проверка во время выполнения; прежде чем доверять такому типу, проверяйте ненадёжный JSON.
Типизация array.map()
Многие, кто ищет «typescript map», имеют в виду метод массива, который преобразует каждый элемент и возвращает новый массив. Его тип выводится из колбэка, так что аннотации нужны редко:
Аннотация возвращаемого типа колбэка ((u): Option => ...) самый понятный способ указать тип результата: пропущенное или написанное с ошибкой свойство в возвращаемом объекте тогда становится ошибкой компиляции прямо в колбэке.
Часто задаваемые вопросы
Как создать Map в TypeScript?
Передайте конструктору типы ключа и значения: const ages = new Map<string, number>(). Если есть начальные записи, типы выводятся: new Map([["ada", 36]]) это Map<string, number>. Голый new Map() без типов и без записей это Map<any, any>, который отключает проверку, поэтому всегда указывайте типы.
Почему Map.get возвращает undefined в TypeScript?
map.get(key) типизирован как V | undefined, потому что ключа может не быть. TypeScript не связывает предшествующий map.has(key) с последующим get, поэтому даже после has нужно обработать undefined: сохраните результат и проверьте его или задайте значение по умолчанию через ??.
Чем Map отличается от объекта в TypeScript?
Map принимает ключи любого типа (включая объекты), сохраняет порядок вставки, имеет size и рассчитан на частые добавления и удаления. У обычного объекта или Record<string, V> ключи только строковые (и символы), он сериализуется в JSON напрямую и поддерживает литеральный синтаксис и деструктуризацию. Используйте Map для динамических коллекций с ключами, а объект для фиксированных форм и данных JSON.
Как преобразовать Map в объект или JSON в TypeScript?
Object.fromEntries(map) превращает Map<string, V> в обычный объект, который затем можно сериализовать через JSON.stringify. JSON.stringify(map) на самом Map даёт "{}", потому что записи Map не являются свойствами. Обратное преобразование: new Map(Object.entries(obj)).
Как типизировать колбэк array.map в TypeScript?
Обычно это не нужно: items.map((item) => item.name) выводит тип item из массива, а тип результата из того, что возвращает колбэк. Чтобы задать тип результата принудительно, передайте его аргументом типа, items.map<string>(...), или аннотируйте возвращаемый тип колбэка.