La réduction de type (Type Narrowing) en TypeScript est le processus par lequel le compilateur "comprend" qu'à un certain endroit du code, une variable a un type plus spécifique en fonction des conditions.
Techniques typiques de réduction :
typeof :function example(x: number | string) { if (typeof x === 'string') { // x: string ici return x.toUpperCase(); } else { // x: number ici return x.toFixed(2); } }
instanceof (pour les classes) :if (dateObj instanceof Date) { // dateObj: Date }
null et undefined :function print(value?: string) { if (value != null) { // value: string console.log(value.length); } }
type Pet = { kind: 'dog'; woof: () => void } | { kind: 'cat'; meow: () => void }; function sound(pet: Pet) { if (pet.kind === 'dog') { pet.woof(); } else { pet.meow(); } }
TypeScript supporte également les fonctions prédicatrices personnalisées :
function isString(x: unknown): x is string { return typeof x === 'string'; }
La réduction rend les vérifications de type plus sûres et le code plus fiable.
Peut-on garantir la réduction de type par des comparaisons simples (par exemple,
==/===), et cela fonctionne-t-il toujours ?
Réponse : Non. Dans tous les cas, TypeScript ne comprend pas le type à partir de comparaisons simples, surtout si la comparaison est trop "floue" ou se fait via des variables/propriétés indirectes. Pour réduire, il est souvent nécessaire d'utiliser des mécanismes explicites (typeof, instanceof, propriétés discriminantes et type guards).
Exemple :
function foo(x: number | string | null) { if (x) { // x: string | number, null n'est déjà plus possible, mais la réduction à un type concret ne se fera pas } }
Histoire
Dans un grand projet TypeScript, la condition de type user.role == 'admin' n'a pas réduit le type de données, et il a fallu vérifier l'existence de la propriété. Les développeurs ont sous-estimé les règles de réduction, ce qui a conduit à des erreurs "Cannot read property ... of undefined".
Histoire
Dans une application mobile, la fonction acceptait soit un objet, soit une chaîne. Par un appel indirect à une fonction modifiant le type, la réduction ne se produisait pas, et sur certains appareils, il y avait un crash lors de l'appel d'une méthode absente de la chaîne. Les tests ont échoué dans des cas rares.
Histoire
Lors de la migration de code de JavaScript vers TypeScript, les fonctions de type guard n'ont pas été implementées, supposant que les vérifications de propriétés réduiraient toujours le type. En conséquence, des objets complexes avec des champs optionnels se sont comportés de manière incorrecte, et des erreurs d'accès aux données imprévisibles sont apparues à l'exécution.