Cicli in TypeScript: for...of, for...in, forEach e tipi | We.Inc
Il tipo che TypeScript assegna alla variabile del ciclo in for, for...of, for...in e forEach, perché obj[key] fallisce dentro for...in e come iterare oggetti e Map in sicurezza.
TypeScript ha gli stessi cicli di JavaScript: for, for...of, for...in, while, do...while e il metodo degli array forEach. Ciò che aggiunge è un tipo per ogni variabile del ciclo. for...of dà il tipo dell'elemento, for...in dà sempre chiavi string, e forEach passa il tipo dell'elemento alla sua callback, che non può interrompersi in anticipo.
Il tipo della variabile del ciclo
Ciclo
Su
Tipo della variabile
for (let i = 0; i < a.length; i++)
number[]
i: number
for (const x of a)
number[]
x: number
for (const k in obj)
{ tea: number; cake: number }
k: string
for (const i in a)
number[]
i: string (non number)
for (const [k, v] of Object.entries(obj))
{ tea: number; cake: number }
k: string, v: number
for (const [k, v] of map)
Map<string, number>
k: string, v: number
a.forEach((x) => ...)
tuple [string, number]
x: string | number
Scegli uno scenario per vedere il codice, il tipo che il compilatore assegna alla variabile del ciclo e cosa stampa il ciclo quando viene eseguito. Due di questi sono errori di compilazione di proposito, perché sono i cicli su cui ci si blocca più spesso in TypeScript.
Scegli uno scenario dai pulsanti sopra il codice.
Leggi la riga del tipo per vedere cosa pensa il compilatore della variabile del ciclo.
Controlla il riquadro di output per vedere cosa stampa il ciclo, o l'errore esatto di tsc quando non compila.
Copia il codice e incollalo nel tuo progetto o nel TypeScript playground.
Preferisci for...of per array, stringhe, Map e Set. La variabile riceve il tipo dell'elemento senza cast, e break e continue funzionano.
Itera un oggetto con Object.entries quando ti servono i valori. Ti consegna coppie tipizzate ed evita l'errore di indicizzazione che produce for...in.
Marca una lista fissa con as const e la variabile del ciclo diventa una union dei suoi valori letterali, che funziona bene negli switch.
Non passare una callback async a forEach aspettandoti che attenda. Usa for...of con await all'interno, oppure Promise.all su un map.
Domande frequenti
Perché la chiave è una stringa in un ciclo for...in?
for...in visita le chiavi string enumerabili, comprese quelle ereditate lungo la prototype chain, e TypeScript non può escludere chiavi extra su un oggetto a runtime. Tipizzare la chiave come keyof T sarebbe una promessa che il linguaggio non può mantenere, quindi resta string.
Come risolvo "Element implicitly has an 'any' type because expression of type 'string' can't be used to index"?
Passa a Object.entries(obj) per avere chiavi e valori insieme, oppure fai un cast delle chiavi una sola volta: for (const k of Object.keys(obj) as (keyof typeof obj)[]). Fai il cast solo quando sai che l'oggetto non ha chiavi extra.
Posso uscire da un forEach in TypeScript?
No. Come in JavaScript, forEach si ferma solo se la callback lancia un'eccezione. Usa for...of con break, oppure some() e find(), che si fermano alla prima corrispondenza.
for...of funziona su una Map in TypeScript?
Sì, con un target ES2015 o successivo. Ogni giro restituisce una coppia [key, value] tipizzata dai generics della Map. Per target più vecchi, attiva downlevelIteration.
Che tipo ha il contatore di un classico ciclo for?
let i = 0 viene inferito come number, e a[i] dà il tipo dell'elemento. Con noUncheckedIndexedAccess attivo, a[i] diventa il tipo dell'elemento o undefined, che è più rigoroso ma più vicino alla realtà.
I cicli sono JavaScript; i tipi sono la parte nuova
TypeScript non aggiunge nessuna sintassi per i cicli. Compila ogni ciclo di questa pagina nello stesso JavaScript, e la guida ai cicli JavaScript su questo sito spiega cosa fa ciascuno a runtime. Qui l'attenzione è sul compilatore: che tipo riceve ogni variabile del ciclo, e gli errori che ne derivano.
Nella maggior parte dei casi ci pensa l'inferenza. Raramente annoti una variabile del ciclo, e se senti il bisogno di farlo, di solito significa che è la collezione stessa ad aver bisogno di un tipo migliore.
Iterare gli oggetti senza errori
Gli oggetti sono il caso scomodo. for...in e Object.keys ti danno entrambi semplici stringhe, e indicizzare un oggetto tipizzato con una semplice stringa è un errore in strict mode. Object.entries lo evita restituendo coppie [string, value], e il tipo del valore deriva dai valori dell'oggetto.
Se l'oggetto è una tabella di lookup che iteri spesso, valuta di tipizzarlo come Record<string, V> o di salvarlo in una Map. Entrambi rendono evidente il tipo della variabile del ciclo e l'errore scompare.
Narrowing dentro un ciclo
Un ciclo su un array di union, come (string | number)[], dà una variabile union. Restringila con typeof nel corpo, e il compilatore tiene traccia del tipo più ristretto per il resto di quel ramo.
Il narrowing non sopravvive tra un giro e l'altro del ciclo né nelle callback eseguite più tardi, quindi ricontrolla dove usi il valore.
Cicli e codice asincrono
Await uno alla volta con for...of
Ogni richiesta aspetta la precedente. L'array dei risultati è tipizzato dal tipo di ritorno della funzione.
Eseguile insieme con Promise.all
map avvia tutte le richieste subito e Promise.all le aspetta tutte. L'ordine dei risultati corrisponde all'ordine degli id.
for...of su array
s: number
Il tipo dell'elemento di number[] passa direttamente nella variabile del ciclo.
for...in su oggetto
k: string
La chiave è una semplice stringa, quindi usarla per indicizzare un oggetto tipizzato fallisce in strict mode.
Object.entries
name: string, price: number
Qui Object.entries restituisce [string, number][], quindi entrambe le metà della coppia sono tipizzate.
Chiavi come keyof
k: "tea" | "cake"
Un cast una tantum dice al compilatore che le chiavi sono esattamente quelle dichiarate. Sicuro solo quando l'oggetto non può avere chiavi extra.
for...in su array
i: string
In for...in gli indici dell'array arrivano come stringhe, quindi assegnarne uno a una variabile number fallisce.
for...of su Map
item: string, qty: number
Destrutturare la entry dà entrambi i tipi generici, nell'ordine in cui le entry sono state aggiunte la prima volta.
forEach su tuple
x: string | number
forEach vede una tuple come un array della union dei tipi dei suoi elementi, quindi restringi con typeof.
Lista as const
as const mantiene i valori letterali, quindi la variabile del ciclo è una union su cui puoi fare uno switch esaustivo.