Dans TypeScript, le pattern matching est réalisé grâce aux "unions discriminées". À chaque objet de l'union est attribué un champ discriminateur obligatoire (généralement une chaîne, par exemple type), par lequel TypeScript différencie les variantes.
Exemple :
type Success = { type: 'success'; data: string }; type Failure = { type: 'failure'; error: string }; type Result = Success | Failure; function handleResult(result: Result) { switch (result.type) { case 'success': // result: Success console.log(result.data); break; case 'failure': // result: Failure console.error(result.error); break; } }
Dans le switch/case ou if sur le champ discriminateur, TypeScript "affinera" le type exactement au bon variant.
Principaux avantages :
Si l'on ajoute un nouveau variant dans l'union discriminée, TypeScript exigera-t-il de mettre à jour tous les switch-case pour traiter le nouveau variant ?
Réponse : Non, seulement si on ajoute explicitement le traitement du variant "impossible". Par exemple, utiliser une fonction never :
Exemple :
function assertNever(x: never): never { throw new Error('Variant inattendue : ' + x); } function handle(r: Result) { switch(r.type) { case 'success': /* ... */; break; case 'failure': /* ... */; break; default: return assertNever(r); // TS générera une erreur si un nouveau type apparaît } }
Histoire
Après avoir étendu le type "Result" avec un nouveau variant ('pending'), à plusieurs endroits de l'application, les anciens switch-case n'ont pas traité ce cas. En conséquence, certaines interfaces ont cessé de fonctionner. L'erreur n'a été remarquée qu'en production, une semaine après la sortie.
Histoire
Une tentative d'utiliser l'union discriminée sans un discriminateur unique (le champ type était dupliqué dans deux types) a conduit à un "flou" des types : TypeScript a cessé deaffiner correctement le type, rendant possible l'accès à des champs inexistants sans erreur de compilation. Plusieurs bugs critiques ont été déployés en production.
Histoire
Dans un projet, le pattern matching a été réalisé via if-else sur plusieurs champs au lieu d'utiliser un seul discriminateur explicite. Cela a compliqué la transition vers la vérification d'exhaustivité avec la fonction never et a rendu le code moins lisible — les switch-case ne fonctionnaient pas correctement et les nouveaux variants "cassaient" la logique existante.