Achtergrond van de vraag: TypeScript breidt de standaard typecontroles uit, waardoor ontwikkelaars hun eigen functies kunnen maken - type guards - die controleren of een object aan een bepaald type voldoet. Dit is noodzakelijk voor het werken met union-types, dynamische structuren en API's, waarbij het type van een waarde kan variëren.
Het probleem is vaak dat standaard typecontroles via typeof en instanceof beperkt zijn tot primitieve types en klassen, en dat je structuren of complexe types zonder dit niet kunt definiëren. Het is belangrijk om de compiler expliciet te kunnen aangeven in welke gevallen de waarde veilig kan worden verkleind tot het gewenste type.
De oplossing is het schrijven van functies die type guards zijn, zoals function isCat(obj: Animal): obj is Cat {...}, waarbij het cruciale punt de type predicate in het geretourneerde type van de functie is.
Voorbeeldcode:
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(); } }
Belangrijke kenmerken:
Is het voldoende om true/false terug te geven in een type guard-functie zodat de compiler het type verkleint?
Nee. Het is belangrijk om het geretourneerde type expliciet aan te geven in de vorm van een type predicate (bijvoorbeeld, pet is Dog), anders zal TypeScript het type van de waarde niet automatisch verkleinen, zelfs als de functie alleen true of false teruggeeft.
Kan een type guard binnen een callback (bijvoorbeeld in filter) worden gebruikt, en zal de verkleining correct werken?
Ja, zolang de type guard correct is geannoteerd, zal de compiler het type van de array-elementen na filter en binnen forEach/callback-functies verkleinen. Maar als de annotatie ontbreekt of onjuist is, zal het resultaat een union type hebben in plaats van een specifiek type.
const pets: Pet[] = [...]; const dogs = pets.filter(isDog); // TypeScript weet dat dogs: Dog[]
Hoe verschillen gebruikersgedefinieerde type guards van standaard typecontroles via typeof, instanceof?
Type guard-functies stellen je in staat om controles voor elke structuur uit te voeren, controles van elke complexiteit te beschrijven, te werken met interfaces zonder klasse, en niet alleen met basis types en klassen.
De functie filtert gebruikers door een type guard zonder type predicate:
function isValidAdmin(user: any): boolean { return user.isAdmin === true; } const admins = users.filter(isValidAdmin); // admins: any[]
Voordelen:
Nadelen:
De functie filtert met een correcte 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[]
Voordelen:
Nadelen: