Contexte de la question :
Avec le développement de TypeScript, la nécessité de déterminer de manière fiable un type plus étroit d'une variable dans des branches logiques est apparue. Les vérifications de type classiques (via typeof ou instanceof) ne sont pas toujours suffisantes, surtout si un objet a une structure ou une hiérarchie complexe. Pour améliorer la sécurité des données et la commodité, TypeScript a mis en œuvre un mécanisme de type prédicats pour créer des guards de type personnalisés.
Problème :
Les vérifications de type ordinaires à l'intérieur des fonctions ne donnent pas au compilateur d'informations sur le type de la variable dans le reste du code si l'on utilise uniquement le résultat true/false. Le compilateur ne "comprend" pas ce qui a été vérifié. Cela entraîne des erreurs implicites à l'exécution lorsque nous tentons par erreur d'accéder à des propriétés inexistantes.
Solution :
Les types prédicats (type predicates) à l'aide du type de substitution sous la forme 'param is Type' permettent au compilateur de comprendre qu'après l'appel de la vérification, on peut travailler avec ce paramètre comme avec un type spécifique. De telles fonctions augmentent la sécurité type et étendent le système de réduction des types pour toutes les tâches complexes.
Exemple de 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 est maintenant un Bird } else { pet.swim(); // OK : pet est maintenant un Fish }
Caractéristiques clés :
Une fonction guard de type peut-elle fonctionner si l'on ne précise pas explicitement dans la signature le type de retour 'param is Type' ?
Non, si l'on ne précise pas explicitement 'param is Type' dans la signature, TypeScript ne pourra pas réduire le type dans les branches de code, malgré la valeur de retour true/false. Le compilateur ne comprendra pas qu'on peut utiliser le paramètre comme un type spécifique.
Exemple de code :
function isFish(animal: Fish | Bird): boolean { return (animal as Fish).swim !== undefined; } // Fonctionne ? if (isFish(pet)) { pet.swim(); // Erreur : La propriété 'swim' n'existe pas }
Peut-on utiliser des types prédicats pour vérifier des valeurs primitives, telles qu'une chaîne ou un nombre ?
Oui, c'est possible, mais on utilise souvent typeof et ces guards deviennent superflus. Néanmoins, rien n'empêche de réaliser un guard personnalisé :
function isString(x: unknown): x is string { return typeof x === "string"; }
Une fonction guard de type assure-t-elle une protection stricte contre les erreurs de type au moment de la compilation ?
Pas totalement. TypeScript s'appuie sur l'implémentation même de la fonction et ne peut pas vérifier la validité de la logique à l'intérieur. Si vous implémentez incorrectement la vérification, le compilateur ne détectera pas l'erreur, des problèmes surviendront à l'exécution.
function isFish(animal: Fish | Bird): animal is Fish { // Incorrect : renvoie toujours true return true; }
** Cas négatif Un développeur a mis en œuvre une fonction prédicat, mais a commis une erreur dans la vérification de la structure, si bien que la fonction retournait toujours true. Le code passait la compilation, mais à l'exécution, une méthode inexistant était appelée.
Avantages :
Inconvénients :
** Cas positif Les fonctions de type prédicat sont correctement mises en œuvre et testées par des tests unitaires sur des valeurs limites et des données erronées.
Avantages :
Inconvénients :