Bucles en TypeScript: for...of, for...in, forEach y tipos | We.Inc
Mira qué tipo da TypeScript a la variable del bucle en for, for...of, for...in y forEach, por qué obj[key] falla dentro de for...in y cómo recorrer objetos y Maps de forma segura.
TypeScript tiene los mismos bucles que JavaScript: for, for...of, for...in, while, do...while y el método de array forEach. Lo que añade es un tipo para cada variable del bucle. for...of da el tipo del elemento, for...in siempre da claves string, y forEach pasa el tipo del elemento a su callback, que no puede salir antes de tiempo.
El tipo de la variable del bucle
Bucle
Sobre
Tipo de la variable
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 (no 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) => ...)
tupla [string, number]
x: string | number
Elige un escenario para ver el código, el tipo que el compilador asigna a la variable del bucle y lo que imprime el bucle al ejecutarse. Dos de ellos son errores de compilación a propósito, porque son los bucles en los que más se atasca la gente con TypeScript.
Elige un escenario con los botones situados sobre el código.
Lee la línea de tipo para ver qué cree el compilador que es la variable del bucle.
Revisa el cuadro de salida para ver lo que imprime el bucle, o el error exacto de tsc cuando no compila.
Copia el código y pégalo en tu proyecto o en el playground de TypeScript.
Prefiere for...of para arrays, strings, Maps y Sets. La variable recibe el tipo del elemento sin conversiones, y break y continue funcionan.
Recorre un objeto con Object.entries cuando necesites los valores. Te da pares tipados y evita el error de indexación que produce for...in.
Marca una lista fija con as const y la variable del bucle se convierte en una unión de sus valores literales, lo que funciona muy bien en sentencias switch.
No pases un callback async a forEach esperando que aguarde. Usa for...of con await dentro, o Promise.all sobre un map.
Preguntas frecuentes
¿Por qué la clave es un string en un bucle for...in?
for...in recorre las claves string enumerables, incluidas las heredadas a través de la cadena de prototipos, y TypeScript no puede descartar que un objeto tenga claves extra en tiempo de ejecución. Tipar la clave como keyof T sería una promesa que el lenguaje no puede cumplir, así que se queda en string.
¿Cómo soluciono "Element implicitly has an 'any' type because expression of type 'string' can't be used to index"?
Cambia a Object.entries(obj) para obtener claves y valores juntos, o convierte las claves una sola vez: for (const k of Object.keys(obj) as (keyof typeof obj)[]). Haz la conversión solo cuando sepas que el objeto no tiene claves extra.
¿Puedo salir de un forEach en TypeScript?
No. Igual que en JavaScript, forEach solo se detiene si el callback lanza un error. Usa for...of con break, o some() y find(), que se detienen en la primera coincidencia.
¿Funciona for...of con un Map en TypeScript?
Sí, con un objetivo ES2015 o posterior. Cada vuelta da un par [key, value] tipado a partir de los genéricos del Map. Para objetivos más antiguos, activa downlevelIteration.
¿Qué tipo tiene el contador de un bucle for clásico?
let i = 0 infiere number, y a[i] da el tipo del elemento. Con noUncheckedIndexedAccess activado, a[i] pasa a ser el tipo del elemento o undefined, que es más estricto pero más fiel a la realidad.
Los bucles son JavaScript; los tipos son lo nuevo
TypeScript no añade ninguna sintaxis de bucles. Compila cada bucle de esta página al mismo JavaScript, y la guía de bucles de JavaScript de este sitio explica qué hace cada uno en tiempo de ejecución. Aquí el foco está en el compilador: qué tipo recibe cada variable del bucle y los errores que se derivan.
La mayoría de las veces la inferencia hace el trabajo. Rara vez anotas una variable de bucle, y si sientes la necesidad, normalmente significa que la propia colección necesita un tipo mejor.
Recorrer objetos sin errores
Los objetos son el caso incómodo. Tanto for...in como Object.keys te dan strings simples, e indexar un objeto tipado con un string simple es un error en modo strict. Object.entries lo evita devolviendo pares [string, value], y su tipo de valor sale de los valores del objeto.
Si el objeto es una tabla de consulta que recorres a menudo, plantéate tiparlo como Record<string, V> o guardarlo en un Map. Ambas opciones hacen evidente el tipo de la variable del bucle y el error desaparece.
Acotar dentro de un bucle
Un bucle sobre un array de unión, como (string | number)[], da una variable de unión. Acótala con typeof dentro del cuerpo y el compilador seguirá el tipo más estrecho durante el resto de esa rama.
El acotamiento no sobrevive entre vueltas del bucle ni dentro de callbacks que se ejecutan después, así que vuelve a comprobar donde uses el valor.
Bucles y código asíncrono
Espera de una en una con for...of
Cada petición espera a la anterior. El array de resultados se tipa a partir del tipo de retorno de la función.
Ejecútalas a la vez con Promise.all
map lanza todas las peticiones a la vez y Promise.all espera a que terminen todas. El orden de los resultados coincide con el orden de los ids.
for...of sobre array
s: number
El tipo del elemento de number[] pasa directamente a la variable del bucle.
for...in sobre objeto
k: string
La clave es un string simple, así que usarla para indexar un objeto tipado falla en modo strict.
Object.entries
name: string, price: number
Aquí Object.entries devuelve [string, number][], así que las dos mitades del par están tipadas.
Claves como keyof
k: "tea" | "cake"
Una conversión puntual le dice al compilador que las claves son exactamente las declaradas. Solo es seguro cuando el objeto no puede llevar claves extra.
for...in sobre array
i: string
En for...in los índices del array llegan como strings, así que asignar uno a una variable number falla.
for...of sobre Map
item: string, qty: number
Desestructurar la entrada da ambos tipos genéricos, en el orden en que se añadieron las entradas por primera vez.
forEach sobre tupla
x: string | number
forEach ve una tupla como un array de la unión de sus tipos de elemento, así que acota con typeof.
Lista as const
as const conserva los valores literales, así que la variable del bucle es una unión que puedes recorrer con un switch exhaustivo.