Pętle w TypeScript: for...of, for...in, forEach i typy | We.Inc
Zobacz, jaki typ TypeScript nadaje zmiennej pętli w for, for...of, for...in i forEach, dlaczego obj[key] zawodzi w for...in i jak bezpiecznie iterować po obiektach i Map.
TypeScript ma te same pętle co JavaScript: for, for...of, for...in, while, do...while oraz metodę tablicową forEach. Dodaje natomiast typ dla każdej zmiennej pętli. for...of daje typ elementu, for...in zawsze daje klucze typu string, a forEach przekazuje typ elementu do callbacku, którego nie da się przerwać wcześniej.
Typ zmiennej pętli
Pętla
Po czym
Typ zmiennej
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 (nie 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) => ...)
krotka [string, number]
x: string | number
Wybierz scenariusz, aby zobaczyć kod, typ, jaki kompilator przypisuje zmiennej pętli, oraz to, co pętla wypisuje po uruchomieniu. Dwa z nich celowo są błędami kompilacji, bo to właśnie przy tych pętlach ludzie najczęściej utykają w TypeScript.
Wybierz scenariusz za pomocą przycisków nad kodem.
Przeczytaj linię z typem, aby zobaczyć, czym według kompilatora jest zmienna pętli.
Sprawdź pole wyniku, aby zobaczyć, co wypisuje pętla, albo dokładny błąd tsc, gdy kod się nie kompiluje.
Skopiuj kod i wklej go do swojego projektu lub do TypeScript playground.
Dla tablic, stringów, Map i Set wybieraj for...of. Zmienna dostaje typ elementu bez rzutowania, a break i continue działają.
Po obiekcie iteruj przez Object.entries, gdy potrzebujesz wartości. Dostajesz otypowane pary i unikasz błędu indeksowania, który powoduje for...in.
Oznacz stałą listę przez as const, a zmienna pętli stanie się unią jej wartości literałowych, co świetnie sprawdza się w instrukcjach switch.
Nie przekazuj asynchronicznego callbacku do forEach, oczekując, że poczeka. Użyj for...of z await w środku albo Promise.all na wyniku map.
Najczęściej zadawane pytania
Dlaczego klucz w pętli for...in jest typu string?
for...in odwiedza przeliczalne klucze tekstowe, także te dziedziczone przez łańcuch prototypów, a TypeScript nie może wykluczyć dodatkowych kluczy w obiekcie w czasie działania. Otypowanie klucza jako keyof T byłoby obietnicą, której język nie może dotrzymać, więc pozostaje string.
Jak naprawić "Element implicitly has an 'any' type because expression of type 'string' can't be used to index"?
Przejdź na Object.entries(obj), aby dostać klucze i wartości razem, albo rzutuj klucze jeden raz: for (const k of Object.keys(obj) as (keyof typeof obj)[]). Rzutuj tylko wtedy, gdy wiesz, że obiekt nie ma dodatkowych kluczy.
Czy mogę przerwać forEach w TypeScript?
Nie. Tak jak w JavaScripcie, forEach zatrzymuje się tylko wtedy, gdy callback rzuci wyjątek. Użyj for...of z break albo some() i find(), które zatrzymują się na pierwszym dopasowaniu.
Czy for...of działa na Map w TypeScript?
Tak, przy docelowej wersji ES2015 lub nowszej. Każde przejście daje parę [key, value] otypowaną na podstawie generyków Map. Dla starszych wersji docelowych włącz downlevelIteration.
Jaki typ ma licznik klasycznej pętli for?
let i = 0 jest inferowane jako number, a a[i] daje typ elementu. Przy włączonym noUncheckedIndexedAccess a[i] staje się typem elementu lub undefined, co jest bardziej rygorystyczne, ale bliższe prawdy.
Pętle to JavaScript, nowością są typy
TypeScript nie dodaje żadnej składni pętli. Każdą pętlę z tej strony kompiluje do tego samego JavaScriptu, a przewodnik po pętlach w JavaScripcie na tej stronie opisuje, co każda z nich robi w czasie działania. Tutaj w centrum jest kompilator: jaki typ dostaje każda zmienna pętli i jakie błędy z tego wynikają.
Zwykle całą pracę wykonuje inferencja. Rzadko adnotujesz zmienną pętli, a jeśli czujesz taką potrzebę, zwykle oznacza to, że sama kolekcja potrzebuje lepszego typu.
Iteracja po obiektach bez błędów
Obiekty to niewygodny przypadek. for...in i Object.keys dają zwykłe stringi, a indeksowanie otypowanego obiektu zwykłym stringiem jest błędem w trybie strict. Object.entries tego unika, zwracając pary [string, value], a typ wartości pochodzi z wartości obiektu.
Jeśli obiekt to tablica przeglądowa, po której często iterujesz, rozważ otypowanie go jako Record<string, V> albo przechowywanie w Map. Oba sposoby czynią typ zmiennej pętli oczywistym, a błąd znika.
Zawężanie wewnątrz pętli
Pętla po tablicy unii, takiej jak (string | number)[], daje zmienną typu unii. Zawęź ją przez typeof w ciele pętli, a kompilator będzie śledził węższy typ do końca tej gałęzi.
Zawężenie nie przetrwa między kolejnymi przejściami pętli ani w callbackach uruchamianych później, więc sprawdzaj ponownie tam, gdzie używasz wartości.
Pętle i kod asynchroniczny
Czekaj na każde po kolei z for...of
Każde żądanie czeka na poprzednie. Tablica wyników jest otypowana na podstawie typu zwracanego funkcji.
Uruchom je razem z Promise.all
map uruchamia wszystkie żądania naraz, a Promise.all czeka na wszystkie. Kolejność wyników odpowiada kolejności ids.
for...of po tablicy
s: number
Typ elementu number[] trafia prosto do zmiennej pętli.
for...in po obiekcie
k: string
Klucz to zwykły string, więc indeksowanie nim otypowanego obiektu zawodzi w trybie strict.
Object.entries
name: string, price: number
Object.entries zwraca tu [string, number][], więc obie połowy pary są otypowane.
Klucze jako keyof
k: "tea" | "cake"
Jednorazowe rzutowanie mówi kompilatorowi, że klucze są dokładnie tymi zadeklarowanymi. Bezpieczne tylko wtedy, gdy obiekt nie może mieć dodatkowych kluczy.
for...in po tablicy
i: string
W for...in indeksy tablicy przychodzą jako stringi, więc przypisanie jednego z nich do zmiennej number zawodzi.
for...of po Map
item: string, qty: number
Destrukturyzacja wpisu daje oba typy generyczne, w kolejności, w jakiej wpisy zostały pierwotnie dodane.
forEach po krotce
x: string | number
forEach widzi krotkę jako tablicę unii typów jej elementów, więc zawężaj przez typeof.
Lista as const
as const zachowuje wartości literałowe, więc zmienna pętli jest unią, którą możesz wyczerpująco obsłużyć w switch.