Menu

Map in TypeScript: Map<K, V>, get, set, has e iterazione

Come usare Map in TypeScript: creare un Map<K, V> tipizzato, perché get restituisce V | undefined, set, has e delete, iterare in ordine di inserimento, chiavi oggetto, conversione da e verso oggetti e JSON, Map vs oggetto vs Record, e come tipizzare array.map().

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Un Map di TypeScript è il Map integrato di JavaScript con chiavi e valori tipizzati: Map<string, number> associa stringhe a numeri. Crealo con new Map<K, V>(), poi usa set, get, has e delete. get restituisce V | undefined, perché la chiave potrebbe mancare.

Sono gli argomenti di tipo a rendere utile un Map in TypeScript: ogni set viene controllato e ogni get restituisce il tipo del valore. L'ultima riga è un errore di compilazione (TS2345); @ts-expect-error fa sì che il blocco venga comunque eseguito.

Questa pagina parla della collezione Map. Se cercavi array.map(), lo trovi nell'ultima sezione.

Creare un Map

I tipi arrivano dagli argomenti di tipo, dalle voci iniziali o da un'annotazione. Le voci iniziali sono un array di tuple [key, value], o qualsiasi altra cosa che le produca.

Due trappole dell'inferenza:

  • new Map() senza argomenti di tipo e senza voci è Map<any, any>. Niente di ciò che inserisci o estrai viene controllato. Scrivi sempre new Map<K, V>().
  • Voci con tipi di valore diversi non producono un'unione. new Map([["a", 1], ["b", "x"]]) è l'errore TS2769 (No overload matches this call). Scrivi il tipo: new Map<string, number | string>([...]).

get restituisce V | undefined

Un Map non può garantire che una chiave esista, quindi get ha tipo V | undefined. Con strict, devi gestire undefined prima di usare il valore come V:

index.ts(4,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'.
  Type 'undefined' is not assignable to type 'number'.

Le soluzioni, dalla più comune alla meno comune:

TypeScript non ricorda che has ha restituito true quando in seguito chiami get. Controllare direttamente il risultato di get è più breve e sicuro. Una non-null assertion, stock.get("apples")!, zittisce l'errore ma non ti protegge se la chiave manca.

Metodi di Map e i loro tipi

MembroTipo su Map<K, V>Note
new Map<K, V>(entries?)Map<K, V>entries: iterabile di [K, V]
set(key, value)thisAggiunge o sostituisce; concatenabile
get(key)V | undefinedundefined se manca
has(key)boolean
delete(key)booleantrue se qualcosa è stato rimosso
clear()voidRimuove tutto
sizenumberUna proprietà, non un metodo
keys(), values()iteratori di K, VEspandili in un array: [...map.keys()]
entries(), for...ofiteratore di [K, V]Ordine di inserimento
forEach((value, key) => ...)voidAttenzione: il valore viene prima

Iterare un Map

Un Map si itera in ordine di inserimento. for...of sul map produce tuple [key, value], con tipo [K, V].

Impostare una chiave esistente aggiorna il valore ma ne mantiene la posizione originale nell'ordine.

Chiavi oggetto e conteggi

Qualsiasi valore può essere una chiave, compresi oggetti e array. Le chiavi si confrontano come con ===: due oggetti con lo stesso contenuto sono chiavi diverse. Un Map è anche il modo standard per contare o raggruppare elementi.

Per usare come chiave il contenuto di un oggetto, ricava invece una chiave stringa o numerica, come user.id o `${x},${y}`.

Map vs oggetto vs Record

Map<K, V>Oggetto / Record<string, V>
Tipi di chiavequalsiasi, confrontati come con ===string (i numeri diventano stringhe), symbol
Tipo per chiave mancanteget restituisce V | undefinedobj[key] è V a meno che noUncheckedIndexedAccess sia attivo
Ordineordine di inserimentoin gran parte ordine di inserimento, ma le chiavi simili a interi vengono prima, in ordine crescente
Dimensionemap.sizeObject.keys(obj).length
Aggiunte e rimozioni frequentiottimizzato per questonon ottimizzato per questo
JSONnon direttamente (JSON.stringify(map) è "{}")diretto
Sintassi letterale, destrutturazionenosì
Chiavi ereditate per errorenessuna"toString" in {} è true

Regola pratica: un Map per una collezione le cui chiavi sono dati (id utente, parole, voci di cache) e cambiano a runtime; un tipo oggetto o Record per un insieme fisso di chiavi note e per tutto ciò che va verso o arriva da JSON. La pagina sul dictionary confronta index signature, Record e Map per le ricerche con chiavi stringa.

Convertire i Map in oggetti e JSON

Le voci di un Map non sono proprietà, quindi JSON.stringify non le vede. Converti passando per Object.fromEntries e Object.entries:

JSON.parse restituisce any, quindi as Record<...> dichiara cosa ti aspetti che siano i dati. Non è un controllo a runtime; valida il JSON non affidabile prima di fidarti di quel tipo.

Tipizzare array.map()

Molte ricerche di "typescript map" riguardano il metodo degli array, che trasforma ogni elemento e restituisce un nuovo array. Il suo tipo viene inferito dalla callback, quindi le annotazioni servono di rado:

Annotare il tipo di ritorno della callback ((u): Option => ...) è il modo più chiaro per dichiarare il tipo del risultato: una proprietà mancante o scritta male nell'oggetto restituito diventa allora un errore di compilazione nella callback.

Domande frequenti

Come si crea un Map in TypeScript?

Passa i tipi di chiave e valore al costruttore: const ages = new Map<string, number>(). Con delle voci iniziali, i tipi vengono inferiti: new Map([["ada", 36]]) è un Map<string, number>. Un new Map() senza tipi e senza voci è Map<any, any>, che disattiva il controllo, quindi dagli sempre dei tipi.

Perché Map.get restituisce undefined in TypeScript?

map.get(key) ha tipo V | undefined perché la chiave potrebbe non esserci. TypeScript non collega un map.has(key) precedente a un get successivo, quindi anche dopo has devi gestire undefined: salva il risultato e controllalo, oppure usa un valore predefinito con ??.

Che differenza c'è tra Map e oggetto in TypeScript?

Un Map accetta chiavi di qualsiasi tipo (oggetti compresi), mantiene l'ordine di inserimento, ha una size ed è fatto per aggiunte e rimozioni frequenti. Un oggetto semplice o un Record<string, V> ha solo chiavi stringa (e symbol), si serializza direttamente in JSON e supporta la sintassi letterale e la destrutturazione. Usa un Map per collezioni dinamiche con chiavi, un oggetto per forme fisse e dati JSON.

Come converto un Map in un oggetto o in JSON in TypeScript?

Object.fromEntries(map) trasforma un Map<string, V> in un oggetto semplice, che JSON.stringify può poi serializzare. JSON.stringify(map) sul Map stesso dà "{}", perché le voci di un Map non sono proprietà. L'operazione inversa è new Map(Object.entries(obj)).

Come tipizzo la callback di array.map in TypeScript?

Di solito non serve: items.map((item) => item.name) inferisce item dall'array e il tipo del risultato da ciò che restituisce la callback. Per forzare un tipo di risultato, passalo come argomento di tipo, items.map<string>(...), oppure annota il tipo di ritorno della callback.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA