Schleifen in TypeScript und die Typen, die sie dir geben
Schleifen in TypeScript
Schleifen in TypeScript: for...of, for...in, forEach und Typen | We.Inc
Sieh, welchen Typ TypeScript der Schleifenvariable in for, for...of, for...in und forEach gibt, warum obj[key] in for...in scheitert und wie du Objekte und Maps sicher durchläufst.
TypeScript hat dieselben Schleifen wie JavaScript: for, for...of, for...in, while, do...while und die Array-Methode forEach. Neu ist ein Typ für jede Schleifenvariable. for...of liefert den Elementtyp, for...in liefert immer String-Schlüssel, und forEach übergibt den Elementtyp an sein Callback, das nicht vorzeitig abbrechen kann.
Der Typ der Schleifenvariable
Schleife
Über
Typ der 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 (nicht 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) => ...)
[string, number]-Tupel
x: string | number
Wähle ein Szenario, um den Code zu sehen, den Typ, den der Compiler der Schleifenvariable zuweist, und was die Schleife bei der Ausführung ausgibt. Zwei davon sind absichtlich Compilerfehler, denn bei genau diesen Schleifen bleiben die meisten in TypeScript hängen.
Wähle über die Buttons oberhalb des Codes ein Szenario aus.
Lies die Typzeile, um zu sehen, was der Compiler für die Schleifenvariable hält.
Im Ausgabefeld siehst du, was die Schleife ausgibt, oder den genauen tsc-Fehler, wenn sie nicht kompiliert.
Kopiere den Code und füge ihn in dein Projekt oder den TypeScript Playground ein.
Nimm für Arrays, Strings, Maps und Sets lieber for...of. Die Variable bekommt den Elementtyp ohne Cast, und break und continue funktionieren.
Durchlaufe ein Objekt mit Object.entries, wenn du die Werte brauchst. Du erhältst typisierte Paare und umgehst den Indexfehler, den for...in erzeugt.
Markiere eine feste Liste mit as const, dann wird die Schleifenvariable zu einer Union ihrer Literalwerte, was gut mit switch-Anweisungen funktioniert.
Übergib forEach kein async-Callback in der Erwartung, dass es wartet. Nutze for...of mit await darin oder Promise.all über ein map.
Häufige Fragen
Warum ist der Schlüssel in einer for...in-Schleife ein String?
for...in besucht aufzählbare String-Schlüssel, auch solche, die über die Prototypkette geerbt sind, und TypeScript kann zur Laufzeit zusätzliche Schlüssel an einem Objekt nicht ausschließen. Den Schlüssel als keyof T zu typisieren wäre ein Versprechen, das die Sprache nicht halten kann, also bleibt er string.
Wie behebe ich "Element implicitly has an 'any' type because expression of type 'string' can't be used to index"?
Wechsle zu Object.entries(obj), um Schlüssel und Werte zusammen zu bekommen, oder caste die Schlüssel einmal: for (const k of Object.keys(obj) as (keyof typeof obj)[]). Caste nur, wenn du weißt, dass das Objekt keine zusätzlichen Schlüssel hat.
Kann ich in TypeScript aus forEach ausbrechen?
Nein. Wie in JavaScript stoppt forEach nur, wenn das Callback eine Exception wirft. Nutze for...of mit break oder some() und find(), die beim ersten Treffer anhalten.
Funktioniert for...of in TypeScript mit einer Map?
Ja, mit einem Ziel von ES2015 oder höher. Jeder Durchlauf liefert ein [key, value]-Paar, typisiert aus den Generics der Map. Für ältere Ziele aktivierst du downlevelIteration.
Welchen Typ hat der Zähler einer klassischen for-Schleife?
let i = 0 wird als number inferiert, und a[i] liefert den Elementtyp. Mit aktiviertem noUncheckedIndexedAccess wird a[i] zum Elementtyp oder undefined, was strenger, aber näher an der Wahrheit ist.
Die Schleifen sind JavaScript, die Typen sind das Neue
TypeScript fügt keine Schleifensyntax hinzu. Es kompiliert jede Schleife auf dieser Seite zu demselben JavaScript, und der Leitfaden zu JavaScript-Schleifen auf dieser Website erklärt, was jede zur Laufzeit tut. Hier liegt der Fokus auf dem Compiler: welchen Typ jede Schleifenvariable bekommt und welche Fehler daraus folgen.
Meistens erledigt die Inferenz die Arbeit. Du annotierst eine Schleifenvariable selten, und wenn du das Bedürfnis hast, braucht meist die Collection selbst einen besseren Typ.
Objekte ohne Fehler durchlaufen
Objekte sind der unbequeme Fall. for...in und Object.keys liefern dir beide einfache Strings, und ein typisiertes Objekt mit einem einfachen String zu indexieren ist im Strict Mode ein Fehler. Object.entries vermeidet das, indem es [string, value]-Paare zurückgibt, deren Werttyp aus den Werten des Objekts stammt.
Ist das Objekt eine Lookup-Tabelle, die du oft durchläufst, typisiere sie als Record<string, V> oder speichere sie in einer Map. Beides macht den Typ der Schleifenvariable offensichtlich, und der Fehler verschwindet.
Eingrenzen innerhalb einer Schleife
Eine Schleife über ein Union-Array wie (string | number)[] liefert eine Union-Variable. Grenze sie im Schleifenrumpf mit typeof ein, und der Compiler verfolgt den engeren Typ für den Rest dieses Zweigs.
Die Eingrenzung überdauert keine weiteren Schleifendurchläufe und gilt nicht in Callbacks, die später laufen. Prüfe also dort erneut, wo der Wert verwendet wird.
Schleifen und asynchroner Code
Mit for...of eins nach dem anderen awaiten
Jede Anfrage wartet auf die vorherige. Das Ergebnis-Array ist aus dem Rückgabetyp der Funktion typisiert.
Mit Promise.all alle gleichzeitig ausführen
map startet jede Anfrage auf einmal, und Promise.all wartet auf alle. Die Reihenfolge der Ergebnisse entspricht der Reihenfolge der ids.
for...of über Array
s: number
Der Elementtyp von number[] fließt direkt in die Schleifenvariable.
for...in über Objekt
k: string
Der Schlüssel ist ein einfacher String, ein typisiertes Objekt damit zu indexieren scheitert also im Strict Mode.
Object.entries
name: string, price: number
Object.entries gibt hier [string, number][] zurück, beide Hälften des Paars sind also typisiert.
Schlüssel als keyof
k: "tea" | "cake"
Ein einmaliger Cast sagt dem Compiler, dass die Schlüssel genau die deklarierten sind. Nur sicher, wenn das Objekt keine zusätzlichen Schlüssel haben kann.
for...in über Array
i: string
Array-Indizes kommen in for...in als Strings an, die Zuweisung an eine number-Variable scheitert also.
for...of über Map
item: string, qty: number
Die Destrukturierung des Eintrags liefert beide generischen Typen, in der Reihenfolge, in der die Einträge zuerst hinzugefügt wurden.
forEach über Tupel
x: string | number
forEach sieht ein Tupel als Array der Union seiner Elementtypen, grenze also mit typeof ein.
Liste mit as const
as const behält die Literalwerte, die Schleifenvariable ist also eine Union, über die du vollständig per switch verzweigen kannst.