Map w TypeScript to wbudowana w JavaScript Map z typowanymi kluczami i wartościami: Map<string, number> mapuje stringi na liczby. Utwórz ją przez new Map<K, V>(), a potem używaj set, get, has i delete. get zwraca V | undefined, bo klucza może brakować.
To argumenty typu sprawiają, że Map jest przydatna w TypeScript: każde set jest sprawdzane, a każde get zwraca typ wartości. Ostatnia linia to błąd kompilacji (TS2345); @ts-expect-error pozwala uruchomić blok mimo to.
Ta strona dotyczy kolekcji Map. Jeśli szukasz array.map(), znajdziesz tę metodę w ostatniej sekcji.
Tworzenie Map
Typy pochodzą z argumentów typu, z początkowych wpisów albo z adnotacji. Początkowe wpisy to tablica krotek [key, value] albo cokolwiek innego, co je zwraca.
Dwie pułapki wnioskowania:
new Map()bez argumentów typu i bez wpisów toMap<any, any>. Nic, co wkładasz lub wyjmujesz, nie jest sprawdzane. Zawsze pisznew Map<K, V>().- Wpisy z różnymi typami wartości nie dają wnioskowanej unii.
new Map([["a", 1], ["b", "x"]])to błąd TS2769 (No overload matches this call). Podaj typ:new Map<string, number | string>([...]).
get zwraca V | undefined
Map nie może zagwarantować, że klucz istnieje, więc get ma typ V | undefined. W trybie strict musisz obsłużyć undefined, zanim użyjesz wartości jako V:
index.ts(4,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
Type 'undefined' is not assignable to type 'number'.
Rozwiązania, od najczęstszego do najrzadszego:
TypeScript nie pamięta, że has zwróciło true, gdy później wywołujesz get. Bezpośrednie sprawdzenie wyniku get jest krótsze i bezpieczne. Asercja non-null, stock.get("apples")!, ucisza błąd, ale nie chroni, gdy klucza brakuje.
Metody Map i ich typy
| Element | Typ w Map<K, V> | Uwagi |
|---|---|---|
new Map<K, V>(entries?) | Map<K, V> | entries: iterowalny zbiór [K, V] |
set(key, value) | this | Dodaje lub zastępuje; można łączyć w łańcuch |
get(key) | V | undefined | undefined, gdy brakuje klucza |
has(key) | boolean | |
delete(key) | boolean | true, jeśli coś usunięto |
clear() | void | Usuwa wszystko |
size | number | Właściwość, nie metoda |
keys(), values() | iteratory K, V | Rozwiń do tablicy: [...map.keys()] |
entries(), for...of | iterator [K, V] | Kolejność wstawiania |
forEach((value, key) => ...) | void | Uwaga: wartość jest pierwsza |
Iteracja po Map
Map iteruje w kolejności wstawiania. for...of po mapie zwraca krotki [key, value] o typie [K, V].
Ustawienie istniejącego klucza aktualizuje wartość, ale zachowuje jej pierwotne miejsce w kolejności.
Obiekty jako klucze i zliczanie
Kluczem może być dowolna wartość, także obiekty i tablice. Klucze są porównywane jak przez ===: dwa obiekty o tej samej zawartości to różne klucze. Map to też standardowy sposób na zliczanie lub grupowanie elementów.
Aby kluczem była zawartość obiektu, utwórz z niej klucz tekstowy lub liczbowy, na przykład user.id albo `${x},${y}`.
Map a obiekt i Record
Map<K, V> | Obiekt / Record<string, V> | |
|---|---|---|
| Typy kluczy | dowolne, porównywane jak przez === | string (liczby stają się stringami), symbol |
| Typ przy braku klucza | get zwraca V | undefined | obj[key] to V, chyba że włączono noUncheckedIndexedAccess |
| Kolejność | kolejność wstawiania | zwykle kolejność wstawiania, ale klucze wyglądające jak liczby całkowite idą pierwsze, rosnąco |
| Rozmiar | map.size | Object.keys(obj).length |
| Częste dodawanie i usuwanie | zoptymalizowana pod to | niezoptymalizowany pod to |
| JSON | nie bezpośrednio (JSON.stringify(map) to "{}") | bezpośrednio |
| Składnia literałów, destrukturyzacja | nie | tak |
| Przypadkowo odziedziczone klucze | brak | "toString" in {} to true |
Praktyczna zasada: Map dla kolekcji, której klucze są danymi (id użytkowników, słowa, wpisy cache) i zmieniają się w trakcie działania programu; typ obiektowy lub Record dla stałego zestawu znanych kluczy i wszystkiego, co trafia do JSON albo z niego pochodzi. Strona o słownikach porównuje sygnatury indeksu, Record i Map przy wyszukiwaniu po kluczach tekstowych.
Konwersja Map na obiekty i JSON
Wpisy Map nie są właściwościami, więc JSON.stringify ich nie widzi. Konwertuj przez Object.fromEntries i Object.entries:
JSON.parse zwraca any, więc as Record<...> określa, jakich danych się spodziewasz. To nie jest sprawdzenie w czasie działania; zweryfikuj niezaufany JSON, zanim zaufasz temu typowi.
Typowanie array.map()
Wiele wyszukiwań "typescript map" dotyczy metody tablic, która przekształca każdy element i zwraca nową tablicę. Jej typ jest wnioskowany z callbacku, więc adnotacje rzadko są potrzebne:
Adnotacja typu zwracanego przez callback ((u): Option => ...) to najczytelniejszy sposób na określenie typu wyniku: brakująca lub źle napisana właściwość w zwracanym obiekcie staje się wtedy błędem kompilacji w callbacku.
Najczęściej zadawane pytania
Jak utworzyć Map w TypeScript?
Przekaż typy klucza i wartości do konstruktora: const ages = new Map<string, number>(). Przy początkowych wpisach typy są wnioskowane: new Map([["ada", 36]]) to Map<string, number>. Samo new Map() bez typów i bez wpisów to Map<any, any>, które wyłącza sprawdzanie, więc zawsze podawaj typy.
Dlaczego Map.get zwraca undefined w TypeScript?
map.get(key) ma typ V | undefined, bo klucza może nie być. TypeScript nie łączy wcześniejszego map.has(key) z późniejszym get, więc nawet po has musisz obsłużyć undefined: zapisz wynik i go sprawdź albo użyj wartości domyślnej z ??.
Czym różni się Map od obiektu w TypeScript?
Map przyjmuje klucze dowolnego typu (także obiekty), zachowuje kolejność wstawiania, ma size i jest zbudowana z myślą o częstym dodawaniu i usuwaniu. Zwykły obiekt lub Record<string, V> ma tylko klucze typu string (i symbol), serializuje się bezpośrednio do JSON i obsługuje składnię literałów oraz destrukturyzację. Używaj Map do dynamicznych kolekcji z kluczami, a obiektu do stałych kształtów i danych JSON.
Jak zamienić Map na obiekt lub JSON w TypeScript?
Object.fromEntries(map) zamienia Map<string, V> na zwykły obiekt, który JSON.stringify może potem zserializować. JSON.stringify(map) na samej Map daje "{}", bo wpisy Map nie są właściwościami. W drugą stronę działa new Map(Object.entries(obj)).
Jak otypować callback array.map w TypeScript?
Zwykle nie trzeba: items.map((item) => item.name) wnioskuje item z tablicy, a typ wyniku z tego, co zwraca callback. Aby wymusić typ wyniku, podaj go jako argument typu, items.map<string>(...), albo dodaj adnotację typu zwracanego do callbacku.