
Dans cet article
- Les enum TypeScript existent en 3 variantes : numériques, chaînes de caractères et hétérogènes
- La directive erasableSyntaxOnly introduite dans TypeScript 5.8 change la donne pour les enums classiques
- Les alternatives comme as const et les unions de types littéraux gagnent du terrain face aux enums traditionnelles
- La conversion d’un enum en chaîne de caractères se fait en 2 lignes grâce à l’indexation inversée
- Un tableau comparatif détaille les performances et cas d’usage de chaque approche : enum, const enum, objet figé et union de types
- Les enums restent pleinement supportées en 2026, malgré les débats récurrents sur leur dépréciation
Sommaire
- Qu’est-ce qu’un enum en TypeScript ?
- Déclarer un enum : syntaxe et variantes
- Convertir un enum en chaîne de caractères
- Enum vs type union vs objet const
- Pourquoi certains développeurs évitent les enums
- erasableSyntaxOnly et avenir des enums
- Bonnes pratiques pour utiliser les enums
- Cas pratiques en projet réel
Quand j’ai commencé à migrer mes projets PHP vers des architectures front modernes avec TypeScript, les enum TypeScript ont été l’un des premiers mécanismes que j’ai adoptés. En tant que développeur habitué aux constantes PHP et aux enums introduites en PHP 8.1, je retrouvais un concept familier. Pourtant, les énumérations TypeScript ont leurs propres subtilités, leurs forces et leurs limites. Dans cet article, je vous propose un tour complet du sujet : de la déclaration basique aux débats actuels sur leur pertinence en 2026.
Qu’est-ce qu’un enum en TypeScript ?
Oui, TypeScript propose bien des enums, et ce depuis ses toutes premières versions. Un enum (abréviation d’énumération) est une structure qui permet de définir un ensemble de constantes nommées. L’idée est simple : au lieu de manipuler des valeurs brutes comme des nombres ou des chaînes éparpillées dans le code, on les regroupe sous un même nom explicite.
Prenons un exemple concret. Dans une application de gestion de commandes, plutôt que d’écrire status === 0 ou status === "pending" un peu partout, on déclare :
enum OrderStatus {
Pending,
Processing,
Shipped,
Delivered,
Cancelled
}
Chaque membre reçoit automatiquement une valeur numérique incrémentale à partir de 0. Ainsi, OrderStatus.Pending vaut 0, OrderStatus.Processing vaut 1, et ainsi de suite. Le code devient lisible, maintenable et surtout typé : le compilateur TypeScript s’assure que seules les valeurs autorisées sont utilisées.
Ce qui différencie l’enum TypeScript d’un simple objet JavaScript, c’est qu’il génère du code JavaScript réel à la compilation. Contrairement aux types et aux interfaces qui disparaissent après transpilation, l’enum produit un objet concret exploitable à l’exécution. C’est à la fois sa force et, comme nous le verrons, l’une des raisons des critiques qu’il reçoit.

Déclarer un enum : syntaxe et variantes
La déclaration d’un enum en TypeScript suit une syntaxe claire et directe. Voici les trois grandes familles que vous rencontrerez.
Enum numérique
C’est la forme la plus courante. Les valeurs sont des nombres entiers attribués automatiquement :
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// On peut aussi définir une valeur de départ
enum HttpCode {
OK = 200,
Created = 201,
BadRequest = 400,
NotFound = 404,
ServerError = 500
}
Quand on spécifie une valeur de départ, les membres suivants sans affectation explicite s’incrémentent automatiquement. C’est pratique pour les codes HTTP, les niveaux de priorité ou tout ensemble de constantes numériques ordonnées.
Enum de chaînes de caractères
Chaque membre reçoit une valeur de type string. Il n’y a pas d’auto-incrémentation ici ; chaque valeur doit être explicitement définie :
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
Les enums de chaînes sont extrêmement utiles en pratique. Elles facilitent le débogage (on lit “RED” plutôt que 0 dans la console) et s’intègrent naturellement avec les API REST, les bases de données et les systèmes de configuration. C’est la variante que j’utilise le plus souvent dans mes projets.
Enum hétérogène
TypeScript autorise le mélange de nombres et de chaînes dans un même enum, même si cette pratique est déconseillée :
enum Mixed {
No = 0,
Yes = "YES"
}
En pratique, les enums hétérogènes créent de la confusion et rendent le typage moins prévisible. Je les évite systématiquement.
Const enum
Le const enum est une variante optimisée qui n’émet aucun code JavaScript à la compilation. Les valeurs sont directement inlinées :
const enum LogLevel {
Debug = 0,
Info = 1,
Warn = 2,
Error = 3
}
// À la compilation, LogLevel.Error devient simplement 3
console.log(LogLevel.Error); // compilé en : console.log(3)
Cette approche offre de meilleures performances car il n’y a aucun objet créé à l’exécution. En revanche, on perd la possibilité de faire du reverse mapping (retrouver le nom à partir de la valeur).
Convertir un enum en chaîne de caractères
La conversion d’un enum TypeScript en string est une opération courante. La méthode dépend du type d’enum utilisé.
Avec un enum de chaînes
C’est le cas le plus simple : la valeur est déjà une chaîne.
enum Fruit {
Apple = "APPLE",
Banana = "BANANA",
Cherry = "CHERRY"
}
const myFruit: string = Fruit.Apple; // "APPLE"
Avec un enum numérique (reverse mapping)
Les enums numériques supportent le reverse mapping, une fonctionnalité qui permet de récupérer le nom du membre à partir de sa valeur :
enum Status {
Active, // 0
Inactive // 1
}
const name: string = Status[0]; // "Active"
const value: number = Status.Active; // 0
Ce mécanisme fonctionne parce que TypeScript génère un objet à double entrée : clé vers valeur et valeur vers clé. Le code JavaScript compilé ressemble à ceci :
var Status;
(function (Status) {
Status[Status["Active"] = 0] = "Active";
Status[Status["Inactive"] = 1] = "Inactive";
})(Status || (Status = {}));
Lister toutes les valeurs d’un enum
Pour itérer sur les membres d’un enum numérique, il faut filtrer les clés numériques du reverse mapping :
enum Role {
Admin,
Editor,
Viewer
}
const names = Object.keys(Role).filter(k => isNaN(Number(k)));
// ["Admin", "Editor", "Viewer"]
const values = Object.values(Role).filter(v => typeof v === "number");
// [0, 1, 2]
Pour un enum de chaînes, c’est plus direct car il n’y a pas de reverse mapping parasite :
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
const allColors = Object.values(Color);
// ["RED", "GREEN", "BLUE"]
Enum vs type union vs objet const
C’est la question qui revient le plus souvent dans la communauté TypeScript : faut-il utiliser un enum, une union de types littéraux ou un objet figé avec as const ? Voici un comparatif détaillé pour trancher.
| Critère | enum | const enum | Union de types | Objet as const |
|---|---|---|---|---|
| Code JS généré | Oui (objet IIFE) | Non (inliné) | Non | Oui (objet simple) |
| Reverse mapping | Oui (numérique) | Non | Non | Manuel |
| Itération runtime | Oui | Non | Non | Oui |
| Tree shaking | Difficile | Parfait | Parfait | Bon |
| Taille du bundle | Plus lourd | Minimal | Zéro | Léger |
| Compatibilité isolatedModules | Limitée | Problématique | Totale | Totale |
| Autocomplétion IDE | Excellente | Excellente | Bonne | Bonne |
| Complexité | Moyenne | Faible | Faible | Moyenne |
L’union de types littéraux est l’alternative la plus légère :
type Direction = "up" | "down" | "left" | "right";
function move(dir: Direction) {
// TypeScript vérifie que dir est bien l'une des 4 valeurs
}
L’objet figé avec as const offre le meilleur des deux mondes : des valeurs accessibles à l’exécution et un typage strict :
const DIRECTION = {
Up: "up",
Down: "down",
Left: "left",
Right: "right"
} as const;
type Direction = typeof DIRECTION[keyof typeof DIRECTION];
// "up" | "down" | "left" | "right"
Dans mes projets récents, j’utilise de plus en plus l’approche as const pour les nouveaux développements, tout en conservant les enums dans le code existant. C’est un équilibre pragmatique qui fonctionne bien en équipe. Pour aller plus loin sur les bonnes pratiques de typage, je vous recommande de consulter la documentation officielle TypeScript sur les enums.

Pourquoi certains développeurs évitent les enums
Les critiques envers les enums TypeScript ne manquent pas, et certaines sont tout à fait légitimes. Voici les principaux reproches que je vois revenir dans la communauté.
Génération de code JavaScript inattendue
Un enum TypeScript est la seule construction de type qui produit du code JavaScript à la compilation. Cela surprend les développeurs habitués au principe selon lequel les types sont « effacés » à la transpilation. Le code généré utilise un pattern IIFE (Immediately Invoked Function Expression) qui alourdit le bundle et complique le tree shaking des bundlers comme Webpack ou Rollup.
Problèmes avec isolatedModules
Si vous utilisez un transpilateur comme Babel, esbuild ou SWC (ce qui est très fréquent avec Vite, Next.js ou les projets React modernes), l’option isolatedModules est activée. Or, les const enum réexportés entre fichiers posent problème dans ce mode, car le transpilateur traite chaque fichier isolément sans connaître la valeur réelle des membres. C’est l’un des soucis concrets que j’ai rencontrés en migrant un projet vers une architecture modulaire avec des enums.
Typage nominal vs structurel
TypeScript utilise un système de types structurel : deux types sont compatibles si leur structure correspond. Les enums numériques font exception en introduisant un comportement quasi nominal, ce qui crée des incohérences :
enum Status { Active, Inactive }
enum Role { Admin, User }
let s: Status = Status.Active;
// s = Role.Admin; // Erreur, même si les deux valent 0
Ce comportement est généralement souhaitable, mais il déroge au paradigme structurel de TypeScript, ce qui déroute certains développeurs.
Verbosité et complexité cachée
Pour un concept simple (un ensemble de constantes), l’enum introduit une complexité cachée : reverse mapping, IIFE, double indexation. Des alternatives comme as const accomplissent la même chose avec moins de « magie » sous le capot. C’est d’ailleurs un point soulevé fréquemment sur les recommandations de performance du wiki TypeScript.
erasableSyntaxOnly et avenir des enums
La question revient régulièrement : les enums sont-elles dépréciées en TypeScript ? La réponse courte est non. Elles sont toujours pleinement supportées et il n’existe aucun plan officiel pour les retirer du langage.
Cependant, l’introduction de l’option erasableSyntaxOnly dans TypeScript 5.8 a relancé le débat. Cette option, conçue pour la compatibilité avec le support natif de TypeScript dans Node.js (via le flag --experimental-strip-types), interdit toute syntaxe TypeScript qui ne peut pas être simplement « effacée » lors de la transpilation.
Concrètement, avec erasableSyntaxOnly: true dans votre tsconfig.json, les enums classiques sont interdites car elles génèrent du code JavaScript. Seules restent autorisées les constructions purement typées qui disparaissent à la compilation.
// tsconfig.json
{
"compilerOptions": {
"erasableSyntaxOnly": true
}
}
// Ceci provoque une erreur de compilation :
enum Status { Active, Inactive }
// Ceci fonctionne (les types sont effaçables) :
type Status = "active" | "inactive";
Cela ne signifie pas que les enums sont dépréciées. Cela signifie que l’écosystème évolue vers des outils qui préfèrent une transpilation simple par effacement. Si vous n’activez pas cette option, les enums fonctionnent exactement comme avant. C’est une tendance à suivre de près, surtout si vous travaillez dans un environnement DevOps où les chaînes de build sont optimisées pour la vitesse.
Bonnes pratiques pour utiliser les enums
Après des années d’utilisation des enums dans des projets TypeScript de tailles variées, voici les règles que j’applique systématiquement.
Privilégiez les enums de chaînes plutôt que les enums numériques. Les valeurs sont explicites dans les logs, les réponses API et les outils de débogage. Quand un collègue voit "SHIPPED" dans la console, c’est immédiatement compréhensible ; 2 ne l’est pas.
Utilisez PascalCase pour le nom de l’enum et PascalCase pour ses membres. C’est la convention recommandée par la documentation officielle TypeScript :
// Bon
enum PaymentMethod {
CreditCard = "CREDIT_CARD",
BankTransfer = "BANK_TRANSFER",
PayPal = "PAYPAL"
}
// À éviter
enum payment_method {
credit_card = "credit_card"
}
Évitez les const enum dans les bibliothèques publiées sur npm. Les consommateurs qui utilisent isolatedModules auront des problèmes. Réservez-les aux applications finales où vous contrôlez toute la chaîne de compilation.
Documentez le choix de structure. Si vous optez pour un enum plutôt qu’une union de types ou un objet as const, expliquez pourquoi dans un commentaire. L’équipe suivante vous remerciera. Ce type de documentation technique est particulièrement précieux dans les contextes de pratiques DevOps où les équipes tournent fréquemment.
Centralisez vos enums dans un fichier dédié ou un module partagé. Avoir des enums dispersés dans 20 fichiers différents devient vite ingérable. Un fichier enums.ts ou un dossier constants/ avec un barrel export facilite la maintenance.
Ne mélangez jamais les types dans un enum hétérogène. Si vous avez besoin de valeurs mixtes, c’est probablement le signe que deux enums distincts seraient plus appropriés.

Cas pratiques en projet réel
Pour illustrer concrètement l’utilisation des enum TypeScript, voici trois scénarios tirés de projets sur lesquels j’ai travaillé.
Gestion des rôles utilisateur
Dans une application SaaS avec un système de gestion de contacts, les rôles utilisateur sont un cas d’usage idéal pour les enums :
enum UserRole {
SuperAdmin = "SUPER_ADMIN",
Admin = "ADMIN",
Manager = "MANAGER",
Member = "MEMBER",
Guest = "GUEST"
}
function canAccessDashboard(role: UserRole): boolean {
return [UserRole.SuperAdmin, UserRole.Admin, UserRole.Manager].includes(role);
}
function canDeleteUsers(role: UserRole): boolean {
return role === UserRole.SuperAdmin;
}
L’enum garantit que les comparaisons de rôles utilisent toujours des valeurs valides. Plus de risque de faute de frappe sur une chaîne littérale.
Machine à états pour un processus de commande
Les enums sont particulièrement adaptés aux machines à états :
enum OrderState {
Draft = "DRAFT",
Confirmed = "CONFIRMED",
Paid = "PAID",
Preparing = "PREPARING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Refunded = "REFUNDED"
}
const TRANSITIONS: Record<OrderState, OrderState[]> = {
[OrderState.Draft]: [OrderState.Confirmed],
[OrderState.Confirmed]: [OrderState.Paid],
[OrderState.Paid]: [OrderState.Preparing, OrderState.Refunded],
[OrderState.Preparing]: [OrderState.Shipped],
[OrderState.Shipped]: [OrderState.Delivered],
[OrderState.Delivered]: [OrderState.Refunded],
[OrderState.Refunded]: []
};
function canTransition(from: OrderState, to: OrderState): boolean {
return TRANSITIONS[from].includes(to);
}
Configuration d’un formulaire dynamique
Dans un projet React avec TypeScript, les enums aident à typer les types de champs d’un formulaire généré dynamiquement :
enum FieldType {
Text = "text",
Email = "email",
Number = "number",
Select = "select",
Checkbox = "checkbox",
Date = "date"
}
interface FormField {
name: string;
label: string;
type: FieldType;
required: boolean;
options?: string[]; // uniquement pour FieldType.Select
}
const contactForm: FormField[] = [
{ name: "fullName", label: "Nom complet", type: FieldType.Text, required: true },
{ name: "email", label: "Courriel", type: FieldType.Email, required: true },
{ name: "subject", label: "Sujet", type: FieldType.Select, required: true, options: ["Support", "Vente", "Autre"] }
];
Ce pattern s’intègre parfaitement avec les bibliothèques de formulaires comme React Hook Form. L’enum assure la cohérence entre le typage et le rendu, et l’autocomplétion de l’IDE accélère le développement. Pour construire ce type d’interface, une bonne maîtrise des outils de développement est essentielle, comme celle que propose une formation Docker pour la conteneurisation de vos environnements de développement.
Enum et appels API
Quand vous travaillez avec des API REST, les enums de chaînes facilitent la sérialisation et la désérialisation :
enum ApiEndpoint {
Users = "/api/v1/users",
Products = "/api/v1/products",
Orders = "/api/v1/orders"
}
async function fetchData<T>(endpoint: ApiEndpoint): Promise<T> {
const response = await fetch(endpoint);
return response.json();
}
L’enum centralise les chemins d’API en un seul endroit. Si un endpoint change, la modification se fait une seule fois. C’est un gain de maintenabilité considérable sur les projets de moyenne et grande envergure.
À retenir
- Préférez les enums de chaînes aux enums numériques pour un débogage plus lisible
- Évaluez l’alternative as const pour les nouveaux projets, surtout avec esbuild ou SWC
- Évitez les const enum dans les bibliothèques npm partagées
- Centralisez vos enums dans un fichier ou module dédié pour faciliter la maintenance
- Testez votre code avec erasableSyntaxOnly activé pour anticiper les futures évolutions de l’écosystème
Questions fréquentes
Les enums existent-elles vraiment en TypeScript ?
Oui, les enums font partie de TypeScript depuis la version 0.9 du langage, sortie en 2013. Elles sont disponibles en trois variantes : numériques, chaînes de caractères et hétérogènes. Le mot-clé enum permet de déclarer un ensemble de constantes nommées qui sont vérifiées à la compilation par le système de types de TypeScript.
Les enums TypeScript sont-elles dépréciées ?
Non, les enums TypeScript ne sont pas dépréciées et restent pleinement supportées en 2026. L’introduction de l’option erasableSyntaxOnly dans TypeScript 5.8 a alimenté cette confusion, mais cette option est facultative. Elle empêche l’utilisation des enums classiques uniquement quand elle est activée, pour faciliter la compatibilité avec les transpilateurs qui fonctionnent par simple effacement de types.
Pourquoi certains développeurs n’aiment pas les enums TypeScript ?
Les critiques portent principalement sur quatre points : les enums génèrent du code JavaScript à la compilation (contrairement aux autres constructions de type), elles compliquent le tree shaking des bundlers, elles posent des problèmes avec l’option isolatedModules utilisée par Babel et esbuild, et elles introduisent un comportement nominal dans un système de types structurel. L’alternative as const ou les unions de types littéraux répondent souvent aux mêmes besoins avec moins de complexité.
Comment déclarer un enum en TypeScript ?
On utilise le mot-clé enum suivi du nom en PascalCase, puis les membres entre accolades. Pour un enum numérique : enum Status { Active, Inactive }. Pour un enum de chaînes : enum Status { Active = "ACTIVE", Inactive = "INACTIVE" }. Pour un const enum qui n’émet pas de code JavaScript : const enum Status { Active, Inactive }.
Quelle est la différence entre un enum et un type union en TypeScript ?
Un enum génère un objet JavaScript réel accessible à l’exécution, avec un reverse mapping pour les enums numériques. Un type union (type Status = "active" | "inactive") est entièrement effacé à la compilation et n’existe qu’au niveau du système de types. L’enum est préférable quand on a besoin d’itérer sur les valeurs à l’exécution ; le type union est plus léger et mieux supporté par les transpilateurs modernes.
Comment convertir un enum TypeScript en chaîne de caractères ?
Pour un enum de chaînes, la valeur est déjà un string : Color.Red retourne directement "RED". Pour un enum numérique, on utilise le reverse mapping : Status[0] retourne "Active". On peut aussi utiliser Object.keys(MonEnum).filter(k => isNaN(Number(k))) pour obtenir la liste de tous les noms de membres.
Ingénieur système et expert hébergement web. Fondateur de web-city.fr, il partage guides pratiques, comparatifs objectifs et outils gratuits pour choisir le bon hébergeur et créer son site WordPress.