Historie van de vraag:
Met de ontwikkeling van TypeScript ontstond de noodzaak om een betrouwbaardere, specifiekere type-structuur van een variabele binnen logische vertakkingen vast te stellen. De klassieke type-controles (via typeof of instanceof) zijn niet altijd voldoende, vooral niet als een object een complexe structuur of hiërarchie heeft. Om de gegevensveiligheid en het gebruiksgemak te verhogen, heeft TypeScript het mechanisme van type predicaten geïmplementeerd voor het creëren van gebruikersgedefinieerde type guards.
Probleem:
Standaard type-controles binnen functies geven de compiler geen informatie over het type van de variabele in de volgende code als alleen het resultaat true/false wordt gebruikt. De compiler "begrijpt" niet wat er precies gecontroleerd is. Dit leidt tot impliciete fouten tijdens runtime wanneer we per ongeluk toegang proberen te krijgen tot niet-bestaande eigenschappen.
Oplossing:
Type-predicaten (type predicates) met behulp van het type-invoegsel in de vorm van 'param is Type' laten de compiler begrijpen dat met deze parameter na de controle kan worden gewerkt als met een specifiek type. Dergelijke functies vergroten de type-veiligheid en breiden het systeem van type-afgrenzingen uit voor allerlei complexe taken.
Voorbeeld code:
interface Bird { fly(): void; feathers: boolean; } interface Fish { swim(): void; fins: number; } function isBird(animal: Bird | Fish): animal is Bird { return (animal as Bird).fly !== undefined; } const pet: Bird | Fish = ...; if (isBird(pet)) { pet.fly(); // OK: pet is nu Bird } else { pet.swim(); // OK: pet is nu Fish }
Belangrijke kenmerken:
Kan een type guard-functie werken zonder expliciet het geretourneerde type 'param is Type' in de handtekening op te geven?
Nee, als je 'param is Type' niet expliciet in de handtekening opgeeft, kan TypeScript het type in de vertakkingen van de code niet verfijnen, ongeacht de teruggegeven waarde true/false. De compiler begrijpt niet dat de parameter als een specifiek type kan worden gebruikt.
Voorbeeld code:
function isFish(animal: Fish | Bird): boolean { return (animal as Fish).swim !== undefined; } // Werkt het? if (isFish(pet)) { pet.swim(); // Fout: Eigenschap 'swim' bestaat niet }
Kan je type predicaten gebruiken voor het controleren van primitieve waarden, zoals een string of een nummer?
Ja, dat kan, maar vaker wordt typeof gebruikt en worden dergelijke guards overbodig. Toch is er niets dat in de weg staat om een gebruikersgedefinieerde guard te implementeren:
function isString(x: unknown): x is string { return typeof x === "string"; }
Biedt een type guard-functie strikte bescherming tegen type-fouten op compileertijd?
Niet volledig. TypeScript vertrouwt op de implementatie van de functie zelf en kan de logica binnenin niet controleren. Als je de controle verkeerd implementeert, begrijpt de compiler de fout niet, wat problemen kan veroorzaken tijdens uitvoering.
function isFish(animal: Fish | Bird): animal is Fish { // Fout: retourneert altijd true return true; }
Negatieve case Een ontwikkelaar implementeerde een predicaatfunctie maar maakte een fout in de controle van de structuur, waardoor de functie altijd true retourneerde. De code ging door de compilatie, maar tijdens runtime kwam een niet-bestaande methode aan bod.
Voordelen:
Nadelen:
** Positieve case** Type predicaat-functies zijn correct geïmplementeerd, getest met unit-tests op randvoorwaarden en foutieve gegevens.
Voordelen:
Nadelen: