For-Of

Il ciclo for-of in TypeScript rappresenta il metodo moderno e preferito per iterare sui valori di strutture dati iterabili. Introdotto in ES6, fornisce una sintassi pulita e sicura per attraversare array, stringhe, Map, Set e qualsiasi oggetto che implementi il protocollo iterabile, operando direttamente sui valori anziché su indici o chiavi.
Fondamenti e Sintassi
Il ciclo for-of itera attraverso i valori di un iterabile, assegnando ciascun valore alla variabile dichiarata. A differenza di for-in che itera sulle chiavi, for-of accede direttamente ai valori, eliminando la necessità di accesso tramite indice.
const numeri = [10, 20, 30, 40];
for (const numero of numeri) {
console.log(numero); // 10, 20, 30, 40
}
const parola = "TypeScript";
for (const carattere of parola) {
console.log(carattere); // T, y, p, e, ...
}
Tipizzazione Automatica
TypeScript inferisce automaticamente il tipo della variabile del ciclo basandosi sul tipo dell’iterabile, garantendo type safety senza annotazioni esplicite nella maggior parte dei casi.
interface Prodotto {
nome: string;
prezzo: number;
}
const prodotti: Prodotto[] = [
{ nome: "Libro", prezzo: 15 },
{ nome: "Penna", prezzo: 2 },
];
for (const prodotto of prodotti) {
// prodotto è automaticamente tipizzato come Prodotto
console.log(prodotto.nome.toUpperCase());
console.log(prodotto.prezzo.toFixed(2));
}
For-Of con Array
For-of è il metodo preferito per iterare su array quando si necessita accesso ai valori. Evita problemi di for-in con proprietà aggiuntive e fornisce sintassi più pulita rispetto a cicli for classici.
const temperature = [18, 22, 25, 19, 21];
// Pulito e diretto
for (const temp of temperature) {
console.log(`${temp}°C`);
}
// Confronto con for tradizionale
for (let i = 0; i < temperature.length; i++) {
console.log(`${temperature[i]}°C`);
}
Iterazione su Stringhe
For-of itera correttamente su caratteri Unicode, gestendo appropriatamente code point multi-byte a differenza dell’accesso tramite indice.
const emoji = "👋🌍";
// For-of: gestisce correttamente emoji
for (const char of emoji) {
console.log(char); // 👋, 🌍
}
// Accesso per indice: può spezzare emoji
for (let i = 0; i < emoji.length; i++) {
console.log(emoji[i]); // Output frammentato
}
For-Of con Map
Map è iterabile e for-of restituisce coppie [chiave, valore] ad ogni iterazione. È possibile destrutturare direttamente la coppia nella dichiarazione della variabile.
const utenti = new Map<string, number>([
["alice", 25],
["bob", 30],
["carol", 28],
]);
for (const [nome, eta] of utenti) {
console.log(`${nome} ha ${eta} anni`);
}
// Iterare solo su chiavi
for (const nome of utenti.keys()) {
console.log(nome);
}
// Iterare solo su valori
for (const eta of utenti.values()) {
console.log(eta);
}
For-Of con Set
Set supporta iterazione diretta sui valori unici contenuti, senza necessità di conversione o accesso indiretto.
const tags = new Set(["typescript", "javascript", "node"]);
for (const tag of tags) {
console.log(tag.toUpperCase());
}
// Rimozione condizionale durante iterazione
const numeri = new Set([1, 2, 3, 4, 5, 6]);
for (const n of numeri) {
if (n % 2 === 0) {
numeri.delete(n); // Sicuro in iterazione Set
}
}
Controllo di Flusso
For-of supporta completamente break e continue, permettendo controllo granulare dell’iterazione a differenza di forEach.
const valori = [5, 12, 8, 130, 44];
// Uscita anticipata con break
for (const valore of valori) {
if (valore > 100) {
console.log("Trovato valore grande:", valore);
break;
}
}
// Salto iterazione con continue
for (const valore of valori) {
if (valore % 2 !== 0) continue;
console.log("Valore pari:", valore);
}
For-Of con Operazioni Asincrone
For-of funziona perfettamente con async/await, permettendo iterazioni sequenziali di operazioni asincrone. Ogni iterazione attende il completamento prima di procedere alla successiva.
const ids = [1, 2, 3, 4, 5];
async function elaboraTutti() {
for (const id of ids) {
const dati = await fetchDati(id);
await processaDati(dati);
console.log(`Completato: ${id}`);
}
}
// Iterazione parallela con for-of e Promise.all
async function elaboraParallelo() {
const promises = [];
for (const id of ids) {
promises.push(fetchDati(id));
}
const risultati = await Promise.all(promises);
for (const risultato of risultati) {
processaDati(risultato);
}
}
For-Await-Of
Per iterabili asincroni, TypeScript supporta for-await-of, che attende automaticamente la risoluzione di Promise durante l’iterazione.
async function* generatoreAsincrono() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
async function elabora() {
for await (const valore of generatoreAsincrono()) {
console.log(valore); // 1, 2, 3 (in sequenza)
}
}
// Utile per stream e lettura file
async function leggiStream(stream: ReadableStream) {
for await (const chunk of stream) {
processaChunk(chunk);
}
}
Destrutturazione nel Ciclo
For-of permette destrutturazione diretta nella dichiarazione della variabile, utile per array di tuple o oggetti.
const coordinate: [number, number][] = [
[10, 20],
[30, 40],
[50, 60],
];
for (const [x, y] of coordinate) {
console.log(`X: ${x}, Y: ${y}`);
}
const utenti = [
{ nome: "Anna", ruolo: "admin" },
{ nome: "Marco", ruolo: "user" },
];
for (const { nome, ruolo } of utenti) {
console.log(`${nome} è ${ruolo}`);
}
Oggetti Iterabili Personalizzati
Qualsiasi oggetto può essere reso iterabile implementando il metodo Symbol.iterator, permettendo uso con for-of.
class Range {
constructor(
private inizio: number,
private fine: number,
private step: number = 1
) {}
*[Symbol.iterator]() {
for (let i = this.inizio; i <= this.fine; i += this.step) {
yield i;
}
}
}
const range = new Range(1, 10, 2);
for (const num of range) {
console.log(num); // 1, 3, 5, 7, 9
}
Entries, Keys, Values
Array forniscono metodi che restituiscono iteratori specializzati, utili quando servono indici oltre ai valori.
const frutti = ["mela", "banana", "arancia"];
// entries(): iteratore [indice, valore]
for (const [indice, frutto] of frutti.entries()) {
console.log(`${indice}: ${frutto}`);
}
// keys(): iteratore degli indici
for (const indice of frutti.keys()) {
console.log(`Indice: ${indice}`);
}
// values(): iteratore dei valori (equivalente a iterare l'array direttamente)
for (const frutto of frutti.values()) {
console.log(frutto);
}
Performance
For-of ha performance comparabile a for tradizionale per la maggior parte dei casi d’uso, con overhead minimo. Per array enormi in scenari critici, for classico può essere marginalmente più veloce.
const grande = new Array(1000000).fill(0);
// Performance simili nella pratica
console.time("for-of");
for (const n of grande) {
n * 2;
}
console.timeEnd("for-of");
console.time("for");
for (let i = 0; i < grande.length; i++) {
grande[i] * 2;
}
console.timeEnd("for");
For-Of vs For-In vs ForEach
Ogni costrutto ha casi d’uso appropriati: for-of per valori di iterabili, for-in per chiavi di oggetti, forEach per effetti collaterali senza necessità di controllo di flusso.
const array = [1, 2, 3];
// for-of: valori, supporta break/continue
for (const val of array) {
if (val === 2) break;
console.log(val);
}
// for-in: indici (sconsigliato per array)
for (const idx in array) {
console.log(idx); // "0", "1", "2" (stringhe)
}
// forEach: nessun controllo di flusso
array.forEach((val) => {
// non può usare break
console.log(val);
});
TypedArray e Buffer
For-of funziona con TypedArray e Buffer, strutture comuni per manipolazione binaria.
const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
for (const byte of bytes) {
console.log(byte.toString(16)); // 48, 65, 6c, 6c, 6f
}
// Con Node.js Buffer
const buffer = Buffer.from("Hello");
for (const byte of buffer) {
console.log(byte); // 72, 101, 108, 108, 111
}
Generatori e Lazy Evaluation
For-of eccelle con generatori, permettendo iterazione lazy di sequenze potenzialmente infinite con valutazione on-demand.
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// Iterazione controllata di sequenza infinita
let count = 0;
for (const num of fibonacci()) {
console.log(num);
if (++count === 10) break; // Primi 10 numeri Fibonacci
}
Conclusioni
Il ciclo for-of rappresenta lo standard moderno per iterazione in TypeScript, offrendo sintassi pulita, type safety automatica, e compatibilità con diverse strutture dati. Supporto completo per controllo di flusso, operazioni asincrone, e destrutturazione lo rendono versatile per la maggior parte degli scenari di iterazione. Rispetto a for-in e forEach, for-of bilancia leggibilità con funzionalità, risultando appropriato come scelta predefinita per iterare su valori di collezioni.