Umgang mit dem Thema: TypeScript erweitert die üblichen Typüberprüfungen, indem es Entwicklern ermöglicht, ihre eigenen Funktionen – type guards – zu erstellen, die überprüfen, ob ein Objekt einem bestimmten Typ entspricht. Dies ist notwendig, um mit Union-Typen, dynamischen Strukturen und APIs zu arbeiten, bei denen der Typ des Wertes variieren kann.
Das Problem ist oft, dass gewöhnliche Typüberprüfungen über typeof und instanceof auf primitive Typen und Klassen beschränkt sind, was es schwierig macht, Strukturen oder komplexe Typen zu bestimmen. Man muss in der Lage sein, dem Compiler explizit anzuzeigen, in welchen Fällen der Wert sicher auf den erforderlichen Typ eingeengt wird.
Die Lösung besteht darin, Wächterfunktionen in der Form function isCat(obj: Animal): obj is Cat {...} zu schreiben, wobei der Schlüsselpunkt der type predicate im Rückgabewert der Funktion ist.
Beispielcode:
interface Dog { bark: () => void; } interface Cat { meow: () => void; } type Pet = Dog | Cat; function isDog(pet: Pet): pet is Dog { return (pet as Dog).bark !== undefined; } function makeSound(pet: Pet) { if (isDog(pet)) { pet.bark(); } else { pet.meow(); } }
Wichtige Merkmale:
Reicht es aus, in der type guard-Funktion true/false zurückzugeben, damit der Compiler den Typ eingrenzt?
Nein. Es ist wichtig, den Rückgabetyp explizit als type predicate (zum Beispiel: pet is Dog) anzugeben, andernfalls wird TypeScript den Wertetyp nicht automatisch eingrenzen, selbst wenn die Funktion nur true oder false zurückgibt.
Kann man type guards innerhalb eines Callback (zum Beispiel in filter) verwenden, und funktioniert die Eingrenzung korrekt?
Ja, wenn der type guard korrekt annotiert ist, wird der Compiler den Typ der Array-Elemente nach filter und innerhalb von forEach/callback-Funktionen eingrenzen. Ist die Annotation jedoch fehlend oder falsch, hat das Ergebnis den Typ-Union anstelle eines konkretisierten Typs.
const pets: Pet[] = [...]; const dogs = pets.filter(isDog); // TypeScript weiß, dass dogs: Dog[]
Wie unterscheiden sich benutzerdefinierte type guards von gewöhnlichen Typüberprüfungen über typeof, instanceof?
Type guard-Funktionen ermöglichen die Überprüfung beliebiger Strukturen, das Beschreiben von Prüfungen beliebiger Komplexität und den Umgang mit Interfaces ohne Klassen, und nicht nur mit grundlegenden Typen und Klassen.
Die Funktion filtert Benutzer und macht dabei einen type guard ohne type predicate:
function isValidAdmin(user: any): boolean { return user.isAdmin === true; } const admins = users.filter(isValidAdmin); // admins: any[]
Vorteile:
Nachteile:
Die Funktion filtert mit korrektem type predicate:
interface Admin { name: string; isAdmin: true; } function isAdmin(user: any): user is Admin { return user && user.isAdmin === true; } const admins = users.filter(isAdmin); // admins: Admin[]
Vorteile:
Nachteile: