frontend / typescript
Union, intersection et literal types
Explication
Ce que vous allez apprendre
- Définir une union de types avec
|et un type littéral pour restreindre les valeurs possibles - Combiner plusieurs types en un seul avec l'intersection
& - Utiliser un literal type pour interdire à la compilation une valeur métier invalide
- Reconnaître le pattern d'union discriminée via un champ commun (
type) - Relier ce chapitre au narrowing, nécessaire pour exploiter une union en toute sécurité
Dans quel contexte ?
Une fonction mettreAJourStatut(commande, nouveauStatut: string) accepte n'importe quelle chaîne de caractères comme statut, y compris des fautes de frappe comme "expedie" au lieu de "expediee". Un bug en production fait qu'une commande reste bloquée car le code qui vérifie statut === "expediee" ne correspond jamais à la valeur mal orthographiée stockée en base. En remplaçant string par le literal type "en_attente" | "expediee" | "livree" | "annulee", TypeScript aurait rejeté toute tentative d'assigner "expedie" à la compilation, avant même que ce bug n'atteigne la production.
Décrire des choix précis plutôt que des types larges
Jusqu'ici, vous avez typé des variables avec des types larges comme string ou number. Mais souvent, la réalité métier est plus précise : un statut de commande ne peut être que "en_attente", "expediee", "livree" ou "annulee" — jamais autre chose. Les literal types permettent de transformer une valeur exacte en contrainte de type, rendant impossible d'écrire une valeur invalide sans provoquer une erreur de compilation.
Prérequis
Cette leçon suppose les types de base et les fonctions typées (leçons 2 et 4) déjà acquis. Le narrowing, mentionné plusieurs fois ici, sera détaillé en profondeur dans la leçon suivante : pas besoin de le maîtriser encore pour comprendre les unions.
Union : un choix parmi plusieurs formes
L'union (symbole |) exprime qu'une valeur est soit ceci, soit cela. C'est l'outil de base pour représenter des alternatives : un identifiant qui peut être un texte ou un nombre, une réponse qui peut être un succès ou une erreur. Pour utiliser une valeur unionée en toute sécurité, il faut d'abord déterminer dans quel cas on se trouve — c'est le rôle du narrowing, exploré dans la prochaine leçon.
| Concept | Symbole | Signification |
|---|---|---|
| Union | | | La valeur est de l'un OU l'autre des types listés |
| Intersection | & | La valeur possède TOUTES les propriétés des types combinés |
| Literal type | valeur exacte ("centre") | Seule cette valeur précise est acceptée |
Intersection : combiner plusieurs formes en une
À l'inverse, l'intersection (symbole &) fusionne plusieurs types en un seul qui possède toutes leurs propriétés réunies. C'est utile pour composer des objets à partir de briques plus petites, par exemple ajouter un horodatage et un identifiant à n'importe quel type existant.
Astuce
Dès qu'une donnée métier n'a qu'un nombre fini de valeurs valides (statut, rôle, catégorie), préférez toujours un literal type ou une union de literal types à un string générique : l'erreur remonte alors à la compilation plutôt qu'en production.
Un aperçu des unions discriminées
La leçon introduit aussi les unions discriminées, où chaque variante possède un champ commun (comme type) qui permet à TypeScript de déterminer automatiquement de quelle forme il s'agit selon la valeur de ce champ. C'est un pattern central en TypeScript, détaillé en profondeur dans la prochaine leçon consacrée au narrowing avancé.
Commandes & code
Union, intersection et literal types
// Union : "l'un OU l'autre"
type Identifiant = string | number;
function afficherId(id: Identifiant) {
console.log(typeof id === "string" ? id.toUpperCase() : id.toFixed(0));
}
// Literal types : une valeur PRÉCISE comme type
type Alignement = "gauche" | "centre" | "droite";
function aligner(texte: string, position: Alignement) {
/* ... */
}
aligner("titre", "centre");
// aligner("titre", "milieu"); // Error : "milieu" n'est pas assignable à Alignement
// Literal numériques et booléens
type DeSix = 1 | 2 | 3 | 4 | 5 | 6;
type Bascule = true;
// Intersection : "l'un ET l'autre" combinés
type Horodate = { creeLe: Date };
type Identifie = { id: string };
type Entite = Horodate & Identifie; // possède les deux ensembles de propriétés
const facture: Entite = { id: "F-001", creeLe: new Date() };
// Union discriminée (aperçu, détaillée en narrowing avancé)
type Forme =
| { type: "cercle"; rayon: number }
| { type: "rectangle"; largeur: number; hauteur: number };
function aire(forme: Forme): number {
switch (forme.type) {
case "cercle":
return Math.PI * forme.rayon ** 2;
case "rectangle":
return forme.largeur * forme.hauteur;
}
}
// Union de littéraux + inférence stricte avec "as const"
const statuts = ["actif", "inactif", "suspendu"] as const;
type Statut = (typeof statuts)[number]; // "actif" | "inactif" | "suspendu"Résumé
- Union (
|) = un choix parmi plusieurs formes ; intersection (&) = fusion de formes. - Les literal types transforment une valeur précise ("centre") en contrainte de type.
as constsur un tableau/objet dérive une union de littéraux sans la réécrire à la main.
Exercices pratiques
Mission : une commande bloquée par une faute de frappe
Objectif : Diagnostiquer un bug causé par un statut typé string, puis le remplacer par un literal type et modéliser une union discriminée.
Contexte
function mettreAJourStatut(commande, nouveauStatut: string) accepte n'importe quelle chaîne. En base, une commande reste bloquée en "expedie" (faute de frappe) au lieu de "expediee", et le code qui vérifie statut === "expediee" ne correspond jamais. Personne n'a été alerté à la compilation. Ta mission : comprendre pourquoi, puis corriger le typage.