Patterns Avancés TypeScript 5 pour un État Applicatif Infaillible
Unions discriminées, types littéraux de templates, paramètres de types const et validation stricte.
1. Paramètres de types const pour l’inférence littérale
Avant TypeScript 5, appeler une fonction générique avec un tableau ou un objet littéral élargissait par défaut le type inféré : `['admin', 'editor', 'viewer']` était inféré comme `string[]`, pas comme le tuple de types littéraux que vous aviez réellement écrit. Pour obtenir une inférence littérale, il fallait ajouter `as const` à chaque site d’appel, ce qui est facile à oublier et impossible à imposer — rien n’empêchait un collègue d’appeler votre fonction sans cela et de perdre silencieusement la précision dont dépendait votre API.
Le modificateur `const` sur un paramètre de type (`function f<const T>(...)`) déplace ce comportement dans la signature même de la fonction. Chaque site d’appel infère désormais comme si `as const` avait été appliqué, sans que l’appelant ait besoin de s’en souvenir. C’est particulièrement utile pour les API qui construisent leur comportement à partir de valeurs littérales — une table de routage, la liste des transitions valides d’une machine à états, un système de permissions énumérant des chaînes de rôles exactes — où `string[]` est trop permissif pour détecter fautes de frappe ou valeurs invalides au site d’appel.
Le gain pratique est de détecter les erreurs là où elles sont introduites, pas là où elles sont utilisées. Un tableau de permissions typé `string[]` laisse passer `adimn` silencieusement ; le même tableau inféré comme le tuple littéral `readonly ['admin', 'editor', 'viewer']` via un paramètre de type const fait bénéficier tout le code en aval qui vérifie l’appartenance aux rôles connus d’une autocomplétion complète et d’une vérification d’exhaustivité, et une faute de frappe devient une erreur de compilation plutôt qu’un bug runtime découvert en production.
2. Unions discriminées exhaustives
Une union discriminée modélise une valeur qui peut prendre plusieurs formes distinctes, marquées par un champ littéral commun — généralement appelé `type`, `status` ou `kind`. Modéliser l’état applicatif de cette façon (`{ status: 'loading' } | { status: 'success', data: T } | { status: 'error', error: string }`) élimine toute une catégorie de bugs qui touchent les applications utilisant des indicateurs faiblement typés comme des booléens `isLoading`, `data` et `error` séparés, qui peuvent en principe être tous incohérents en même temps — loading et error à true simultanément, data présent alors que status indique error, etc.
Le véritable gain vient de la vérification d’exhaustivité dans les instructions `switch`. Lorsque vous faites un switch sur le champ discriminant et traitez chaque variante, TypeScript peut prouver qu’aucun cas n’a été oublié — et surtout, il génèrera une erreur de compilation dès qu’une nouvelle variante est ajoutée à l’union et oubliée dans un switch qui la traite. C’est ce mécanisme qui transforme « on a oublié de gérer le nouvel état à trois endroits » d’un incident en production en un soulignement rouge dans l’éditeur, avant même l’ouverture de la PR.

