frontend / typescript
Template literal types
Explication
Ce que vous allez apprendre
- Construire une union de chaînes typées avec un template literal type
- Calculer automatiquement toutes les combinaisons valides à partir de plusieurs unions
- Combiner
inferavec un template literal type pour parser une chaîne structurée - Utiliser les transformations natives
Uppercase,Lowercase,Capitalize,Uncapitalize - Générer des noms de handlers d'événements typés à partir d'une liste d'événements
Dans quel contexte ?
Une équipe frontend a un système de classes CSS utilitaires (btn-sm-primaire, btn-lg-danger...) construites en concaténant une taille et une couleur. Un développeur introduit une faute de frappe, btn-md-priamire, qui compile sans problème puisque la prop est typée en string, et la classe CSS n'a simplement aucun effet visuel — un bug silencieux découvert seulement en test manuel. En typant la prop avec le template literal type `btn-${Taille}-${Couleur}`, TypeScript aurait rejeté cette faute de frappe à la compilation, avant même d'exécuter le moindre test.
Construire des types à partir de chaînes de caractères
Les template literal types appliquent, au niveau des types, la même syntaxe que les templates littéraux de JavaScript, les chaînes avec des accolades interpolées. Au lieu de produire une chaîne de caractères concrète à l'exécution, TypeScript génère ici une union de tous les types de chaînes possibles selon les combinaisons de littéraux fournies.
Prérequis
Cette leçon suppose les literal types (leçon 6) et infer (leçon 13) bien acquis : un template literal type combine directement ces deux notions au niveau des chaînes de caractères.
Un générateur de combinaisons exhaustif
Si vous combinez une taille (sm, md, lg) et une couleur (primaire, danger) dans un template comme btn-${Taille}-${Couleur}, TypeScript calcule automatiquement toutes les combinaisons valides, six au total dans cet exemple, et refuse toute chaîne qui n'en fait pas partie. C'est extrêmement utile pour typer des noms de classes CSS, des noms d'événements ou des clés d'API générées de façon prévisible.
| Transformation native | Effet |
|---|---|
Uppercase<T> | Met la chaîne en majuscules |
Lowercase<T> | Met la chaîne en minuscules |
Capitalize<T> | Met une majuscule à la première lettre |
Uncapitalize<T> | Met une minuscule à la première lettre |
Piège fréquent
Typer une prop de classe CSS avec string au lieu d'un template literal type laisse passer n'importe quelle faute de frappe (btn-md-priamire) sans la moindre erreur à la compilation. Dès que les valeurs possibles sont prévisibles par combinaison, un template literal type détecte ces fautes immédiatement.
Parser une chaîne au niveau des types
Combiné à infer, vu dans une leçon précédente, un template literal type peut aussi parser une chaîne structurée pour en extraire des fragments typés — par exemple décomposer une route d'API comme /api/utilisateurs/42 en ses composants ressource et id, directement au niveau du système de types.
Des utilitaires de manipulation de chaîne intégrés
TypeScript fournit aussi nativement quatre transformations de chaîne au niveau des types : Uppercase, Lowercase, Capitalize et Uncapitalize. Elles se combinent naturellement aux template literal types, par exemple pour générer des noms de handlers d'événements à partir d'une simple liste d'événements.
Commandes & code
Template literal types
// Combine des types littéraux en chaînes typées
type Taille = "sm" | "md" | "lg";
type Couleur = "primaire" | "danger";
type ClasseBouton = `btn-${Taille}-${Couleur}`;
// "btn-sm-primaire" | "btn-sm-danger" | "btn-md-primaire" | ... (6 combinaisons)
const classe: ClasseBouton = "btn-md-danger"; // OK
// const invalide: ClasseBouton = "btn-xl-danger"; // Error : "xl" n'existe pas
// Cas pratique : typer des noms d'évènements custom
type EvenementApp = "connexion" | "deconnexion" | "erreur";
type NomHandler = `on${Capitalize<EvenementApp>}`;
// "onConnexion" | "onDeconnexion" | "onErreur"
interface Props {
onConnexion?: () => void;
onDeconnexion?: () => void;
onErreur?: (e: Error) => void;
}
// Extraction via infer combiné à un template literal
type ExtraireRoute<T> = T extends `/api/${infer Ressource}/${infer Id}`
? { ressource: Ressource; id: Id }
: never;
type Route = ExtraireRoute<"/api/utilisateurs/42">;
// { ressource: "utilisateurs"; id: "42" }
// Génération de clés d'objet CSS-in-JS typées
type PropsEspacement = "margin" | "padding";
type Direction = "Top" | "Right" | "Bottom" | "Left";
type CleEspacement = `${PropsEspacement}${Direction}`;
type StylesEspacement = { [K in CleEspacement]?: number };
const style: StylesEspacement = { marginTop: 8, paddingLeft: 16 };
// Utilitaires natifs de manipulation de chaîne au niveau des types
type Cri = Uppercase<"attention">; // "ATTENTION"
type Chuchotement = Lowercase<"BONJOUR">; // "bonjour"
type Titre = Capitalize<"typescript">; // "Typescript"
type Reste = Uncapitalize<"TypeScript">; // "typeScript"
// Construire un chemin d'accès typé pour un objet imbriqué (autocomplétion type-safe)
type Chemins<T, Prefixe extends string = ""> = {
[K in keyof T & string]: T[K] extends object
? Chemins<T[K], `${Prefixe}${K}.`> | `${Prefixe}${K}`
: `${Prefixe}${K}`;
}[keyof T & string];
interface Config {
serveur: { port: number; hote: string };
debug: boolean;
}
type CheminConfig = Chemins<Config>;
// "serveur" | "serveur.port" | "serveur.hote" | "debug"Résumé
- Les template literal types génèrent une union exhaustive de toutes les combinaisons possibles.
- Combinés à
infer, ils parsent des chaînes structurées directement au niveau des types. Uppercase/Lowercase/Capitalize/Uncapitalizesont fournis nativement par TypeScript.
Exercices pratiques
Mission : éliminer les fautes de frappe dans les classes CSS utilitaires
Objectif : Remplacer une prop de classe CSS typée string par un template literal type qui rend une faute de frappe impossible à compiler.
Contexte
Une équipe frontend construit des classes CSS utilitaires (btn-sm-primaire, btn-lg-danger...) en concaténant une taille et une couleur. Une prop typée string a laissé passer btn-md-priamire, une faute de frappe qui compile mais n'a aucun effet visuel. Ta mission : rendre cette faute de frappe détectable à la compilation.