Exploitez les template literal types pour générer des unions par produit cartésien et parser des littéraux de chaîne avec infer.
Ouvrir cette leçon dans KodokonLes template literal types appliquent la syntaxe des littéraux de gabarit au monde des types. Interpolez une union et le compilateur calcule le produit cartésien : chaque combinaison possible devient un membre du résultat. C'est l'outil qui permet de typer des clés d'événements, des routes ou des tokens de design system au caractère près.
type Lang = "fr" | "en";
type Theme = "light" | "dark";
type ThemeKey = `${Lang}-${Theme}`;
// "fr-light" | "fr-dark" | "en-light" | "en-dark"La vraie puissance apparaît combinée à infer : un template literal type devient un motif de filtrage sur les chaînes. Le compilateur découpe le littéral d'entrée pour faire correspondre chaque segment ; face à plusieurs découpages possibles, la première occurrence du séparateur gagne. Ajoutez la récursivité et vous obtenez de véritables parseurs évalués à la compilation.
type Split<S extends string, Sep extends string> =
S extends `${infer Head}${Sep}${infer Rest}`
? [Head, ...Split<Rest, Sep>]
: [S];
type Parts = Split<"a.b.c", ".">;
// ["a", "b", "c"]Ce motif brille pour extraire les paramètres d'une route au format Express. Chaque appel récursif consomme un segment :param, et l'union finale énumère tous les noms de paramètres, exploitables ensuite dans un Record pour typer la signature du handler.
type Params<Path extends string> =
Path extends `${string}:${infer P}/${infer Rest}`
? P | Params<Rest>
: Path extends `${string}:${infer P}`
? P
: never;
type RouteParams = Params<"/users/:id/posts/:postId">;
// "id" | "postId"TypeScript fournit quatre utilitaires intrinsèques - Uppercase, Lowercase, Capitalize, Uncapitalize - implémentés directement dans le compilateur (mot-clé intrinsic), donc impossibles à réécrire soi-même. Autre pattern de production : string & {}. Dans une union, string absorberait les littéraux et l'éditeur perdrait l'auto-complétion ; l'intersection avec {} crée un type équivalent mais distinct, que le compilateur ne réduit pas.
type Method = "get" | "post";
type Handler = `on${Capitalize<Method>}`;
// "onGet" | "onPost"
type KnownColor = "red" | "green" | "blue";
type Color = KnownColor | (string & {});
declare function paint(color: Color): void;
paint("red"); // suggere par l'auto-completion
paint("#ff8800"); // accepte egalementA compte 3 membres et B en compte 4, combien de membres contient ${A}-${B} ?Capitalize appliqué au littéral « hello world » ?KnownColor | (string & {}) plutôt que KnownColor | string ?