Ein Type Guard ist eine Prüfung zur Laufzeit, die TypeScript versteht, und damit engt er den Typ im geprüften Zweig ein. typeof, instanceof und in sind eingebaute Guards; für alles andere schreibst du eine Funktion, deren Rückgabetyp ein Type Predicate ist, value is Type.
isUser gibt zur Laufzeit ein einfaches Boolean zurück. Der Rückgabetyp value is User sagt dem Compiler, was ein Ergebnis true beweist, und jedes if (isUser(x)) engt x dann auf User ein.
Eingebaute Type Guards
Diese Prüfungen engen ohne Hilfsfunktion ein:
| Guard | Beispiel | Verwenden für |
|---|---|---|
typeof | typeof x === "number" | primitive Werte und Funktionen |
instanceof | x instanceof Date | Klasseninstanzen |
in | "email" in x | Unions aus Objekten, Eigenschaften von unknown-Objekten |
Array.isArray | Array.isArray(x) | Arrays |
| Gleichheit | x === null, x.kind === "circle" | null/undefined, Literal-Tags |
| Truthiness | if (x) | null und undefined entfernen |
Alle laufen als gewöhnliches JavaScript. TypeScript ergänzt das Narrowing: Es liest die Prüfung und passt den Typ in jedem Zweig an. Die vollständige Liste der Formen steht auf der Seite zu Type Narrowing. Ein eigener Guard ist für Prüfungen gedacht, die nicht in einen Ausdruck passen oder die du wiederverwenden willst.
Ein Type Predicate schreiben
Ein Type Predicate hat die Form parameterName is Type und ersetzt boolean als Rückgabetyp. Das Narrowing wirkt in beide Richtungen: true engt auf Type ein, und false entfernt Type aus einer Union.
Übergibst du einen Guard an filter, bekommst du ein korrekt typisiertes Array. Seit TypeScript 5.5 leitet der Compiler ein Predicate auch aus einfachen Arrow Functions ab, sodass pets.filter((p) => p.kind === "cat") ohne benannten Guard Cat[] zurückgibt.
Der Typ des Predicates muss zum Typ des Parameters passen: function f(x: string): x is number ist der Fehler TS2677, A type predicate's type must be assignable to its parameter's type.
Der Compiler vertraut deinem Guard
TypeScript prüft, dass ein Guard ein Boolean zurückgibt. Es prüft nicht, ob das Boolean stimmt. Ein Guard, der für die falschen Werte true liefert, lässt die Typen lügen, und das Programm scheitert zur Laufzeit ohne Compilerfehler.
data.price.toFixed(2) wirft zur Laufzeit TypeError: Cannot read properties of undefined (reading 'toFixed'). Der Compiler hat data.price als number akzeptiert, weil der Guard es so gesagt hat. Prüfe jede Eigenschaft, auf die sich der restliche Code verlässt, und halte Guards klein, getestet und nah an dem Typ, den sie beschreiben.
Prüfen, ob ein Objekt einen Typ hat
Das ist die Frage hinter den meisten eigenen Guards: Daten kommen als unknown an (aus JSON.parse, fetch, localStorage, einer Nachricht), und du musst wissen, ob sie zu deinem Interface passen. Das Rezept:
typeof value === "object" && value !== null(ein Objekt, nichtnull)."prop" in valuefür jede Pflichteigenschaft. Aufunknownfügtindie Eigenschaft dem Typ alsunknownhinzu.typeof value.prop === "..."(oder ein verschachtelter Guard) für den Typ jeder Eigenschaft.Array.isArray(value.items) && value.items.every(isItem)für Arrays.
Bei großen oder tief verschachtelten Formen wird das Schreiben von Hand mühsam. Schema-Bibliotheken wie Zod oder Valibot lassen dich die Form einmal beschreiben und liefern dir sowohl die Prüfung zur Laufzeit als auch den TypeScript-Typ.
Assertion-Funktionen: asserts value is Type
Eine Assertion-Funktion wirft, wenn die Prüfung scheitert, und kehrt sonst normal zurück. Ihr Rückgabetyp ist asserts value is Type (oder asserts condition), und alles nach dem Aufruf ist eingeengt, ohne dass ein if nötig ist.
Eine Regel bringt viele ins Stolpern: Eine Assertion-Funktion muss über einen Namen mit ausdrücklichem Typ aufgerufen werden. Eine const Arrow Function ohne Annotation, const check = (v: unknown): asserts v is string => {...}, ergibt beim Aufruf den Fehler TS2775, Assertions require every name in the call target to be declared with an explicit type annotation. Nimm eine function-Deklaration oder annotiere die Konstante mit einem Funktionstyp.
Guards vs Assertions vs Casts
| Werkzeug | Prüfung zur Laufzeit? | Engt ein | Bei Fehlschlag |
|---|---|---|---|
Eingebauter Guard (typeof, in...) | Ja | im Zweig | nimmt den anderen Zweig |
Funktion mit value is T | Ja (dein Code) | im Zweig | nimmt den anderen Zweig |
Funktion mit asserts value is T | Ja (dein Code) | nach dem Aufruf | wirft |
value as T | Nein | den Ausdruck | nichts: der falsche Typ breitet sich aus |
Eine Typ-Assertion (as) ändert den Typ, ohne etwas zu prüfen. An einer Grenze, an der Daten von außen kommen, ist ein Guard oder eine Assertion-Funktion die sichere Variante derselben Idee.
Guards mit this in Klassen
Eine Methode kann das Objekt, auf dem sie aufgerufen wird, mit this is Type einengen. Das ist in Klassenhierarchien praktisch:
class FileNode {
constructor(public name: string) {}
isDirectory(): this is DirectoryNode {
return this instanceof DirectoryNode;
}
}
class DirectoryNode extends FileNode {
children: FileNode[] = [];
}
function count(node: FileNode): number {
return node.isDirectory() ? node.children.length : 0; // node: DirectoryNode in the true branch
}
Häufig gestellte Fragen
Was ist ein Type Guard in TypeScript?
Jede Prüfung zur Laufzeit, die TypeScript nutzt, um einen Typ einzuengen: typeof x === "string", x instanceof Date, "id" in x, Array.isArray(x) oder der Aufruf einer Funktion, deren Rückgabetyp ein Type Predicate wie x is User ist. Im geprüften Zweig hat die Variable den engeren Typ.
Wie prüfe ich in TypeScript, ob ein Objekt einen bestimmten Typ hat?
Typen existieren zur Laufzeit nicht, also prüfst du die Eigenschaften: Schreibe eine Funktion isUser(value: unknown): value is User, die typeof value === "object", value !== null und jede Pflichteigenschaft mit in und typeof prüft. Nach if (isUser(x)) ist x als User typisiert. Bei Klassen reicht x instanceof MyClass.
Was bedeutet "value is Type" in TypeScript?
Das ist ein Type Predicate, verwendet als Rückgabetyp einer Funktion. Die Funktion gibt zur Laufzeit weiterhin ein Boolean zurück, aber wenn sie true liefert, engt TypeScript das Argument beim Aufruf auf Type ein, und bei false auf die übrigen Member der Union. Der Compiler prüft den Funktionsrumpf nicht, die Prüfung muss also stimmen.
Was ist der Unterschied zwischen einem Type Guard und einer Assertion-Funktion?
Ein Type Guard (x is T) gibt ein Boolean zurück und engt in einem if ein. Eine Assertion-Funktion (asserts x is T) gibt nichts zurück und wirft, wenn die Prüfung scheitert, sodass alles nach dem Aufruf ohne if eingeengt ist. Nimm Guards für Verzweigungen und Assertions für: Das muss gelten, sonst abbrechen.
Kann ich in TypeScript prüfen, ob ein Objekt ein Interface implementiert?
Nicht direkt: Interfaces werden entfernt, und instanceof akzeptiert sie nicht. Schreibe einen Type Guard, der die Eigenschaften des Interfaces prüft, oder ergänze eine Literal-Eigenschaft als Tag (kind: "user") und vergleiche sie. Schema-Bibliotheken wie Zod erzeugen Prüfung und Typ aus einer Definition.