Loops no TypeScript: for...of, for...in, forEach e tipos | We.Inc
Veja o tipo que o TypeScript dá à variável do loop em for, for...of, for...in e forEach, por que obj[key] falha dentro do for...in e como percorrer objetos e Maps com segurança.
O TypeScript tem os mesmos loops do JavaScript: for, for...of, for...in, while, do...while e o método de array forEach. O que ele acrescenta é um tipo para cada variável de loop. for...of dá o tipo do elemento, for...in sempre dá chaves string, e forEach passa o tipo do elemento ao callback, que não pode ser interrompido antes do fim.
O tipo da variável do loop
Loop
Sobre
Tipo da variável
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 (não 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
Escolha um cenário para ver o código, o tipo que o compilador atribui à variável do loop e o que o loop imprime ao rodar. Dois deles são erros de compilação de propósito, porque são os loops em que as pessoas mais travam no TypeScript.
Escolha um cenário nos botões acima do código.
Leia a linha de tipo para ver o que o compilador acha que a variável do loop é.
Confira a caixa de saída para ver o que o loop imprime, ou o erro exato do tsc quando não compila.
Copie o código e cole no seu projeto ou no playground do TypeScript.
Prefira for...of para arrays, strings, Maps e Sets. A variável recebe o tipo do elemento sem cast, e break e continue funcionam.
Percorra um objeto com Object.entries quando precisar dos valores. Ele entrega pares tipados e evita o erro de indexação que o for...in produz.
Marque uma lista fixa com as const e a variável do loop vira uma union dos valores literais, o que funciona bem em switch.
Não passe um callback async para o forEach esperando que ele aguarde. Use for...of com await dentro, ou Promise.all sobre um map.
Perguntas frequentes
Por que a chave é string em um loop for...in?
for...in visita chaves string enumeráveis, incluindo as herdadas pela cadeia de protótipos, e o TypeScript não consegue descartar chaves extras em um objeto em tempo de execução. Tipar a chave como keyof T seria uma promessa que a linguagem não consegue cumprir, então ela continua string.
Como corrijo "Element implicitly has an 'any' type because expression of type 'string' can't be used to index"?
Mude para Object.entries(obj) para pegar chaves e valores juntos, ou faça o cast das chaves uma vez: for (const k of Object.keys(obj) as (keyof typeof obj)[]). Só faça o cast quando souber que o objeto não tem chaves extras.
Dá para sair de um forEach no TypeScript?
Não. Como no JavaScript, o forEach só para se o callback lançar um erro. Use for...of com break, ou some() e find(), que param na primeira correspondência.
for...of funciona em um Map no TypeScript?
Sim, com target ES2015 ou posterior. Cada volta entrega um par [key, value] tipado a partir dos generics do Map. Para targets mais antigos, ligue downlevelIteration.
Que tipo tem o contador de um for clássico?
let i = 0 infere number, e a[i] dá o tipo do elemento. Com noUncheckedIndexedAccess ligado, a[i] vira o tipo do elemento ou undefined, o que é mais rígido, porém mais fiel à realidade.
Os loops são JavaScript; os tipos são a novidade
O TypeScript não adiciona nenhuma sintaxe de loop. Ele compila todos os loops desta página para o mesmo JavaScript, e o guia de loops em JavaScript deste site cobre o que cada um faz em tempo de execução. Aqui o foco é o compilador: que tipo cada variável de loop recebe e os erros que vêm disso.
Na maior parte do tempo a inferência resolve. Você raramente anota uma variável de loop, e se sentir necessidade, geralmente é sinal de que a própria coleção precisa de um tipo melhor.
Percorrendo objetos sem erros
Objetos são o caso chato. for...in e Object.keys entregam strings simples, e indexar um objeto tipado com uma string simples é um erro no strict mode. Object.entries evita isso retornando pares [string, value], e o tipo do valor vem dos valores do objeto.
Se o objeto é uma tabela de consulta que você percorre com frequência, considere tipá-lo como Record<string, V> ou guardá-lo em um Map. Os dois deixam o tipo da variável do loop óbvio e o erro desaparece.
Estreitando tipos dentro de um loop
Um loop sobre um array de union, como (string | number)[], dá uma variável union. Estreite com typeof dentro do corpo, e o compilador acompanha o tipo mais estreito pelo resto daquele branch.
O estreitamento não sobrevive entre voltas do loop nem dentro de callbacks que rodam depois, então verifique de novo onde o valor é usado.
Loops e código assíncrono
Aguarde um de cada vez com for...of
Cada requisição espera a anterior. O array de resultados é tipado pelo tipo de retorno da função.
Rode todas juntas com Promise.all
map inicia todas as requisições de uma vez e Promise.all espera por todas. A ordem dos resultados segue a ordem dos ids.
for...of em array
s: number
O tipo do elemento de number[] vai direto para a variável do loop.
for...in em objeto
k: string
A chave é uma string simples, então usá-la para indexar um objeto tipado falha no strict mode.
Object.entries
name: string, price: number
Aqui Object.entries retorna [string, number][], então as duas metades do par são tipadas.
Chaves como keyof
k: "tea" | "cake"
Um cast único diz ao compilador que as chaves são exatamente as declaradas. Seguro só quando o objeto não pode ter chaves extras.
for...in em array
i: string
Os índices do array chegam como strings no for...in, então atribuir um a uma variável number falha.
for...of em Map
item: string, qty: number
Desestruturar a entrada dá os dois tipos genéricos, na ordem em que as entradas foram adicionadas pela primeira vez.
forEach em tupla
x: string | number
O forEach vê uma tupla como um array da union dos tipos dos elementos, então estreite com typeof.
Lista as const
as const mantém os valores literais, então a variável do loop é uma union que você pode tratar por completo em um switch.