ProgrammationArchitecte TypeScript

Comment le pattern matching est-il réalisé dans TypeScript grâce aux unions discriminées ? Comment structurer correctement les types et quels pièges existent ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

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 :

  • Typage strict — on ne peut pas accéder à un champ inexistant.
  • Vérification d'exhaustivité — si toutes les variantes ne sont pas traitées, une erreur peut parfois se produire (peut être explicitement forcée).

Question piège

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 } }

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet.


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.