Menu

Ciclo for in TypeScript: forEach, for...of e for...in

Tutti i modi di scrivere un ciclo in TypeScript e i tipi che ti dà ciascuno: il classico ciclo for, for...of su array, map e stringhe, for...in e le sue chiavi stringa, forEach (niente break, niente await), cicli tipizzati sulle chiavi di un oggetto, e while.

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

TypeScript ha gli stessi cicli di JavaScript: for, for...of, for...in, while, do...while, e il metodo degli array forEach. Ciò che TypeScript aggiunge sono i tipi: la variabile di un for...of ha il tipo degli elementi, e una chiave di for...in è sempre una string.

Per la maggior parte dei cicli sugli array, for...of è quello da scegliere: si legge bene, supporta break, continue e await, e non richiede annotazioni.

Il classico ciclo for

for (init; condition; update) è il ciclo per contare, avanzare di più di uno alla volta, o andare all'indietro. TypeScript deduce il contatore dal suo valore iniziale.

Dichiara il contatore con let, non con var. Con let, ogni iterazione ha il suo binding, quindi una callback creata nel ciclo vede il valore di quell'iterazione.

for...of: i valori di array, stringhe e Map

for...of funziona su qualsiasi iterabile, e la variabile del ciclo riceve il tipo corrispondente. Per avere anche l'indice, cicla su array.entries(), che produce tuple [index, value].

break esce dal ciclo e continue passa all'elemento successivo, esattamente come in un ciclo for. I dettagli a runtime (iterabili, generatori) sono nella pagina JavaScript su for...of e for...in.

forEach: niente break, niente await

forEach chiama una funzione per ogni elemento e restituisce undefined. I parametri della callback sono tipizzati in base all'array, quindi non servono annotazioni. Due cose che non può fare:

  • Fermarsi prima. break dentro la callback è l'errore di compilazione TS1107, Jump target cannot cross function boundary. Un return termina solo la chiamata corrente, quindi si comporta come continue.
  • Aspettare lavoro asincrono. forEach ignora la promise restituita da una callback async.

Il tranello dell'async è facile da non notare, perché compila senza alcun avviso:

Le callback di forEach finiscono nell'ordine dei timer (10, 20, 30) quando il ciclo è già andato avanti. La versione con for...of le esegue in sequenza. Per un lavoro in parallelo che vuoi comunque aspettare, usa await Promise.all(items.map(async (item) => { ... })).

for...in: le chiavi sono stringhe

for...in cicla sui nomi delle proprietà enumerabili di un oggetto. Il tipo della chiave è sempre string, anche per gli array, dove le chiavi sono gli indici come stringhe. Questo lo rende scomodo per indicizzare un oggetto tipizzato:

index.ts(5,22): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; age: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ name: string; age: number; }'.

TypeScript tipizza la chiave come string di proposito: a runtime un oggetto può avere più proprietà di quelle elencate dal suo tipo (la tipizzazione strutturale ne ammette di extra), e for...in percorre anche le proprietà enumerabili ereditate. Quindi non può promettere che la chiave sia "name" | "age". Le soluzioni sono nella prossima sezione. Evita del tutto for...in sugli array.

Ciclare su chiavi e valori di un oggetto

Object.entries dà coppie [key, value] ed è il modo abituale di ciclare su un oggetto. Object.keys restituisce string[] per lo stesso motivo di for...in, quindi quando sai che l'oggetto non ha chiavi in più, fai un'asserzione sul tipo della chiave.

Per dati le cui chiavi non sono note in anticipo, tipizza l'oggetto come Record<string, number> o usa una Map; entrambi sono spiegati nella pagina sui dizionari.

while e do...while

while controlla la condizione prima di ogni passaggio; do...while esegue il corpo una volta prima di controllare. Sono adatti ai cicli che non percorrono una collezione, come leggere finché una condizione non è vera. Il narrowing funziona anche lì: dopo un controllo !== undefined nella condizione, la variabile è ristretta nel corpo.

Etichette, break e continue nei cicli annidati

Un semplice break esce solo dal ciclo più interno. Per uscire da un ciclo esterno, dagli un'etichetta e indica l'etichetta:

Quale ciclo usare

ObiettivoCiclo
Ogni elemento di un arrayfor (const x of arr)
Elemento e indicefor (const [i, x] of arr.entries()) o arr.forEach((x, i) => ...)
Fermarsi primafor...of con break, oppure find, some, every
Aspettare ogni passofor...of con await all'interno
Costruire un nuovo arraymap, filter (non un ciclo)
Contare o avanzare di nfor (let i = 0; ...; i += n)
Chiavi e valori di un oggettofor (const [k, v] of Object.entries(obj))
Voci di una Mapfor (const [k, v] of map)
Finché vale una condizionewhile

Domande frequenti

Come si scrive un ciclo for in TypeScript?

Come in JavaScript: for (let i = 0; i < 5; i++) { ... }; TypeScript deduce i come number. Per scorrere gli elementi di un array, for (const item of items) è più corto, e item riceve il tipo degli elementi dell'array.

Come si esce da un forEach in TypeScript?

Non si può: break dentro la callback è l'errore di compilazione TS1107 (Jump target cannot cross function boundary), e return termina solo la chiamata corrente della callback, come continue. Usa for...of con break, oppure some/find/every, che per costruzione si fermano prima.

Qual è la differenza tra for...of e for...in in TypeScript?

for...of visita i valori di un iterabile (elementi di un array, caratteri di una stringa, voci di una Map) con i loro tipi reali. for...in visita le chiavi delle proprietà enumerabili di un oggetto, sempre tipizzate string, compresi gli indici degli array come stringhe. Usa for...of per gli array e Object.entries per gli oggetti.

Come si scorrono chiavi e valori di un oggetto in TypeScript?

Usa for (const [key, value] of Object.entries(obj)). key è una string e value è l'unione dei tipi delle proprietà. Se ti serve key tipizzata come keyof typeof obj, fai un'asserzione: (Object.keys(obj) as (keyof typeof obj)[]), perché TypeScript tipizza di proposito Object.keys come string[].

Si può usare await dentro forEach?

Compila, ma forEach non aspetta le promise restituite dalla callback, quindi il ciclo finisce prima del lavoro. Usa for...of con await all'interno per un lavoro sequenziale, oppure await Promise.all(items.map(async (x) => ...)) per eseguirlo in parallelo.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA