frontend / typescript
Enums et const assertions
Explication
Ce que vous allez apprendre
- Déclarer un enum numérique et un enum à valeurs explicites
- Expliquer pourquoi un
enumclassique génère du code JavaScript à l'exécution - Identifier les limitations d'un
enumavec les bundlers modernes (isolatedModules) - Construire l'alternative recommandée : un objet
as constcombiné àtypeof/keyof - Utiliser
as constpour figer un tableau ou un objet littéral en types précis
Dans quel contexte ?
Une équipe migre son projet vers Vite, qui utilise esbuild pour transpiler chaque fichier indépendamment (option isolatedModules). Le build échoue avec une erreur sur un enum Statut { EnAttente, Expediee } utilisé dans plusieurs fichiers : esbuild ne peut pas transpiler un enum sans connaître le contenu des autres fichiers du projet. En remplaçant l'enum par const Statut = { EnAttente: "en_attente", Expediee: "expediee" } as const; associé à type Statut = typeof Statut[keyof typeof Statut];, le projet redevient compatible avec isolatedModules, sans rien perdre en confort d'utilisation.
Nommer un ensemble fini de valeurs
Certaines données ne prennent qu'un nombre limité de valeurs possibles : une direction (haut, bas, gauche, droite), un statut de commande, un code HTTP. Les enums (énumérations) sont l'outil historique de TypeScript pour nommer ce genre d'ensemble fini, en donnant un nom lisible à chaque valeur plutôt que de manipuler des nombres ou des chaînes brutes dans le code.
Prérequis
Cette leçon suppose les literal types et les unions (leçon 6) bien compris : l'alternative moderne aux enums repose entièrement sur la combinaison d'un objet figé et d'une union de types littéraux dérivée.
Un compromis à connaître
Ce que beaucoup découvrent en travaillant avec les enums, c'est qu'ils ne sont pas gratuits : un enum classique génère un véritable objet JavaScript à l'exécution, ce qui ajoute un peu de poids au bundle final et empêche certains outils de compilation modernes, comme esbuild ou swc utilisés par la plupart des bundlers actuels, de fonctionner correctement avec l'option isolatedModules. La variante const enum résout le problème de poids en étant inlinée directement dans le code compilé, mais introduit d'autres limitations techniques.
| Approche | Code généré à l'exécution | Compatible isolatedModules |
|---|---|---|
enum classique | Objet JavaScript complet | Non |
const enum | Valeurs inlinées | Non |
Objet as const + typeof/keyof | Objet simple, minimal | Oui |
Piège fréquent
Utiliser un enum classique dans un projet configuré avec isolatedModules: true (la configuration par défaut de la plupart des bundlers modernes comme Vite) provoque une erreur de build. Préférez le pattern objet as const pour tout nouveau projet.
L'alternative moderne recommandée
La communauté TypeScript s'est largement tournée vers un pattern alternatif : un objet simple figé avec as const, dont on dérive ensuite une union de types littéraux via typeof et keyof. Ce pattern offre le même confort qu'un enum côté code, sans générer de surcoût à l'exécution et sans les limitations de compatibilité — c'est aujourd'hui le choix par défaut recommandé pour les nouveaux projets.
Figer des structures avec as const
Plus largement, as const peut être appliqué à n'importe quel tableau ou objet littéral pour le rendre en lecture seule et transformer chacune de ses valeurs en type littéral précis plutôt qu'en type large — un outil que vous retrouverez régulièrement, notamment dans la leçon sur satisfies.
Commandes & code
Enums et const assertions
// Enum numérique : valeurs auto-incrémentées à partir de 0
enum Direction {
Haut, // 0
Bas, // 1
Gauche, // 2
Droite, // 3
}
function deplacer(direction: Direction) {
/* ... */
}
deplacer(Direction.Haut);
// Enum avec valeurs explicites
enum CodeHttp {
Ok = 200,
NonTrouve = 404,
ErreurServeur = 500,
}
// Enum de chaînes : plus lisible en debug/logs, pas de valeur "fantôme"
enum StatutCommande {
EnAttente = "EN_ATTENTE",
Expediee = "EXPEDIEE",
Livree = "LIVREE",
}
console.log(StatutCommande.Livree); // "LIVREE", pas un simple nombre
// const enum : inliné à la compilation, aucun objet JS généré (plus performant)
const enum Taille {
Petit,
Moyen,
Grand,
}
let t = Taille.Grand; // compilé en "let t = 2;" — aucune référence à un objet Taille
// Alternative moderne recommandée : union littérale + "as const"
const STATUTS = {
EN_ATTENTE: "EN_ATTENTE",
EXPEDIEE: "EXPEDIEE",
LIVREE: "LIVREE",
} as const;
type StatutCommandeV2 = (typeof STATUTS)[keyof typeof STATUTS];
// "EN_ATTENTE" | "EXPEDIEE" | "LIVREE" — zéro overhead runtime, tree-shakable
// as const sur un tableau : chaque élément devient un type littéral, tuple readonly
const ROLES = ["admin", "editeur", "lecteur"] as const;
type Role = (typeof ROLES)[number]; // "admin" | "editeur" | "lecteur"
// ROLES.push("invite"); // Error : ROLES est readonly
// as const sur un objet complexe
const THEME = {
couleurs: { primaire: "#0055ff", secondaire: "#ff8800" },
espacement: 8,
} as const;
// THEME.espacement = 16; // Error : readonly| Approche | Overhead runtime | Tree-shakable | Recommandé |
|---|---|---|---|
enum | objet JS généré | non | cas legacy |
const enum | aucun (inliné) | oui | limité (incompatible isolatedModules) |
union + as const | aucun | oui | oui, par défaut |
Résumé
enumgénère un vrai objet JS ;const enuml'inline mais casse avecisolatedModules.- Le pattern
as const+ union littérale est le standard moderne recommandé. as constfige aussi les tableaux et objets en lecture seule, récursivement.
Exercices pratiques
Mission : réparer un build esbuild cassé par un enum
Objectif : Diagnostiquer pourquoi un enum classique casse un build isolatedModules, puis le remplacer par le pattern objet as const recommandé.
Contexte
Une équipe migre son build vers Vite (esbuild), qui transpile chaque fichier isolément via l'option isolatedModules. Le build échoue sur enum Statut { EnAttente, Expediee } utilisé dans plusieurs fichiers. Ta mission : comprendre pourquoi, puis migrer vers le pattern objet as const recommandé.