Menu

Map w TypeScript: Map<K, V>, get, set, has i iteracja

Jak używać Map w TypeScript: tworzenie typowanego Map<K, V>, dlaczego get zwraca V | undefined, set, has i delete, iteracja w kolejności wstawiania, obiekty jako klucze, konwersja do obiektu i JSON oraz z powrotem, Map a obiekt i Record oraz typowanie array.map().

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

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 to Map<any, any>. Nic, co wkładasz lub wyjmujesz, nie jest sprawdzane. Zawsze pisz new 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

ElementTyp w Map<K, V>Uwagi
new Map<K, V>(entries?)Map<K, V>entries: iterowalny zbiór [K, V]
set(key, value)thisDodaje lub zastępuje; można łączyć w łańcuch
get(key)V | undefinedundefined, gdy brakuje klucza
has(key)boolean
delete(key)booleantrue, jeśli coś usunięto
clear()voidUsuwa wszystko
sizenumberWłaściwość, nie metoda
keys(), values()iteratory K, VRozwiń do tablicy: [...map.keys()]
entries(), for...ofiterator [K, V]Kolejność wstawiania
forEach((value, key) => ...)voidUwaga: 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 kluczydowolne, porównywane jak przez ===string (liczby stają się stringami), symbol
Typ przy braku kluczaget zwraca V | undefinedobj[key] to V, chyba że włączono noUncheckedIndexedAccess
Kolejnośćkolejność wstawianiazwykle kolejność wstawiania, ale klucze wyglądające jak liczby całkowite idą pierwsze, rosnąco
Rozmiarmap.sizeObject.keys(obj).length
Częste dodawanie i usuwaniezoptymalizowana pod toniezoptymalizowany pod to
JSONnie bezpośrednio (JSON.stringify(map) to "{}")bezpośrednio
Składnia literałów, destrukturyzacjanietak
Przypadkowo odziedziczone kluczebrak"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.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ