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.
breakdentro la callback è l'errore di compilazione TS1107,Jump target cannot cross function boundary.Unreturntermina solo la chiamata corrente, quindi si comporta comecontinue. - Aspettare lavoro asincrono.
forEachignora la promise restituita da una callbackasync.
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
| Obiettivo | Ciclo |
|---|---|
| Ogni elemento di un array | for (const x of arr) |
| Elemento e indice | for (const [i, x] of arr.entries()) o arr.forEach((x, i) => ...) |
| Fermarsi prima | for...of con break, oppure find, some, every |
| Aspettare ogni passo | for...of con await all'interno |
| Costruire un nuovo array | map, filter (non un ciclo) |
| Contare o avanzare di n | for (let i = 0; ...; i += n) |
| Chiavi e valori di un oggetto | for (const [k, v] of Object.entries(obj)) |
| Voci di una Map | for (const [k, v] of map) |
| Finché vale una condizione | while |
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.