Un enum TypeScript est un ensemble nommé de constantes. enum Direction { Up, Down, Left, Right } crée à la fois un type, Direction, et un objet à l'exécution dont vous accédez aux membres par Direction.Up. Les membres sont numérotés à partir de 0, sauf si vous leur donnez des valeurs, et les enums string attribuent à chaque membre une chaîne lisible.
Les enums font partie des rares fonctionnalités de TypeScript qui ne sont pas que des types : un enum devient un véritable objet JavaScript quand le code est compilé.
Enums numériques
Sans initialiseur, les membres reçoivent 0, 1, 2 et ainsi de suite. Donnez un nombre au premier membre et les suivants continuent à partir de lui. Vous pouvez aussi fixer explicitement chaque valeur, ce qui est le choix sûr quand les nombres sont stockés en base de données ou envoyés sur le réseau.
S'appuyer sur la numérotation automatique convient aux valeurs qui ne quittent jamais le programme. Si l'ordre des membres peut changer et que les nombres sont enregistrés quelque part, insérer un membre au milieu renumérote en silence tout ce qui le suit.
Ce que produit un enum à la compilation
Les types sont effacés, mais pas un enum. Voici le JavaScript que TypeScript émet pour un enum numérique et un enum string :
enum Direction { Up, Down, Left, Right }
enum Status { Active = "ACTIVE", Inactive = "INACTIVE" }
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
var Status;
(function (Status) {
Status["Active"] = "ACTIVE";
Status["Inactive"] = "INACTIVE";
})(Status || (Status = {}));
Direction["Up"] = 0 renvoie 0, donc Direction[0] = "Up" est défini dans la même instruction. Un enum numérique fonctionne ainsi dans les deux sens : du nom vers le nombre, et du nombre vers le nom. C'est le reverse mapping. Les enums string ne vont que des noms vers les valeurs.
L'objet Direction affiché a huit clés : les quatre noms et les quatre nombres. Cela compte dès que vous le parcourez.
Enums string
Chaque membre d'un enum string doit avoir une valeur de chaîne explicite. Les valeurs apparaissent telles quelles dans les logs, le JSON et les bases de données, ce qui rend les enums string plus faciles à déboguer que les nombres.
Un enum string est nominal d'une manière qui surprend : une chaîne simple ne lui est pas assignable, même quand le texte correspond à la valeur d'un membre.
index.ts(7,5): error TS2820: Type '"ACTIVE"' is not assignable to type 'Status'. Did you mean 'Status.Inactive'?
(La suggestion du message est une supposition du compilateur, fausse ici ; la correction est Status.Active.) Dans l'autre sens, une valeur Status peut être utilisée partout où un string est attendu. Quand les valeurs arrivent sous forme de chaînes, depuis du JSON ou un formulaire, convertissez-les avec une vérification comme celle de la section sur la vérification des valeurs, plus bas.
Utiliser un enum comme type
Le nom de l'enum est un type dont les valeurs sont ses membres. Combiné à switch, TypeScript vérifie que chaque membre est traité quand la fonction doit renvoyer une valeur :
Si un nouveau membre est ajouté à Shape sans nouveau case, sides ne compile plus et signale TS2366, Function lacks ending return statement and return type does not include 'undefined'. La page switch montre la vérification exhaustive plus stricte, basée sur never.
Les dernières lignes montrent une vraie faiblesse des enums numériques. Un littéral numérique qui ne correspond à aucun membre, const level: Level = 99, est une erreur de compilation (TS2322), mais toute valeur typée number est acceptée, donc 57 passe. Les enums string n'ont pas cette faille.
Parcourir un enum
Un enum est un objet à l'exécution, donc Object.keys, Object.values et Object.entries fonctionnent. Pour un enum string, ils renvoient exactement les membres. Pour un enum numérique, ils renvoient aussi les entrées du reverse mapping, que vous filtrez :
Pour typer une variable comme « l'un des noms de membres de l'enum », utilisez keyof typeof Direction, qui est l'union "Up" | "Down" | "Left" | "Right". Direction[name] récupère alors la valeur en toute sécurité de typage.
Un enum string n'a pas de reverse mapping : pour obtenir le nom d'un membre à partir de sa valeur, cherchez dans les entrées. Object.entries(Status).find(([, v]) => v === "ACTIVE")?.[0] vaut "Active", ou undefined quand aucun membre n'a cette valeur.
Vérifier qu'une valeur appartient à un enum
Les données venues de l'extérieur du programme sont un simple string ou number. Un type guard les compare aux valeurs de l'enum et les affine en type enum :
Évitez raw as Status sur une entrée non fiable : l'assertion compile, mais rien n'est vérifié à l'exécution, et "DELETED" traverserait le programme typé comme un Status valide.
const enum
const enum demande au compilateur de supprimer l'enum et d'écrire la valeur de chaque membre là où il est utilisé. Il n'y a aucun objet à l'exécution, donc rien ne peut être parcouru ni faire l'objet d'un reverse mapping.
const enum économise quelques octets et une lecture de propriété, mais il suppose que le compilateur voie la déclaration de l'enum quand il compile chaque fichier qui l'utilise. Les outils qui transpilent un fichier à la fois, comme Babel et swc, ne peuvent pas voir un const enum déclaré dans un autre fichier ; le type stripping de Node refuse les const enums comme tous les autres enums ; et avec isolatedModules ou verbatimModuleSyntax, TypeScript signale l'erreur TS2748 quand vous utilisez un const enum provenant d'un fichier de déclaration. La plupart du code applicatif n'a pas besoin de const enums.
Enum, type union ou objet as const
Il existe trois façons courantes de définir un ensemble fixe de valeurs :
enum | Union de littéraux | Objet as const | |
|---|---|---|---|
| Existe à l'exécution | oui, un objet | non | oui, un objet simple |
| Parcourir les valeurs | Object.values (numérique : filtrer) | non, rien à parcourir | Object.values |
Accepte un simple "red" | non (enums string) | oui | oui |
Accès nommé X.Red | oui | non | oui |
| Reverse mapping | enums numériques uniquement | non | non |
| Fonctionne avec le type stripping de Node | non | oui | oui |
Autorisé par erasableSyntaxOnly | non | oui | oui |
| Syntaxe supplémentaire à apprendre | règles des enums, const enums | aucune | le motif typeof |
Beaucoup d'équipes choisissent aujourd'hui par défaut une union de littéraux de chaîne, et passent à l'objet as const quand elles ont besoin des valeurs à l'exécution (pour les parcourir ou construire une liste déroulante). Les raisons : les unions sont de purs types et disparaissent de la sortie ; elles acceptent les chaînes simples que livrent le JSON et les API ; et les enums sont la seule brique du TypeScript courant qui ne soit pas « du JavaScript plus des types effaçables ».
Ce dernier point est devenu concret. Node exécute directement les fichiers .ts en retirant les types, et un enum n'est pas quelque chose qu'il peut retirer :
node status.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode
L'option --experimental-transform-types de Node permet d'exécuter les enums, et l'option de compilation erasableSyntaxOnly signale chaque enum par l'erreur TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., ce qui permet à un projet de les interdire d'emblée. Voir exécuter TypeScript pour le fonctionnement du type stripping. Rien de tout cela ne rend les enums mauvais : le code compilé avec tsc ou un bundler les exécute sans problème, et une base de code qui utilise déjà des enums a peu à gagner à les convertir.
Questions fréquentes
Qu'est-ce qu'un enum en TypeScript ?
Un ensemble nommé de constantes qui est à la fois un type et un objet à l'exécution : enum Direction { Up, Down } vous permet d'écrire Direction.Up et d'utiliser Direction comme type de paramètre. Contrairement à la plupart des fonctionnalités de TypeScript, un enum n'est pas effacé : il se compile en un objet JavaScript qui existe à l'exécution.
Comment parcourir un enum en TypeScript ?
Pour un enum string, Object.values(MyEnum) donne les valeurs et Object.keys(MyEnum) les noms. Un enum numérique contient aussi des entrées de reverse mapping ("0": "Up"), qu'il faut filtrer : Object.keys(Direction).filter((k) => isNaN(Number(k))) ne donne que les noms. Un const enum ne peut pas être parcouru, car il n'existe pas à l'exécution.
Comment convertir une chaîne en valeur d'enum en TypeScript ?
Vérifiez la chaîne par rapport aux valeurs de l'enum dans un type guard : function isStatus(s: string): s is Status { return (Object.values(Status) as string[]).includes(s); }. Après la vérification, s est de type Status. Un simple s as Status compile, mais ne vérifie rien à l'exécution.
Faut-il utiliser un enum ou un type union en TypeScript ?
Beaucoup d'équipes préfèrent une union de littéraux de chaîne (type Status = "active" | "inactive"), ou un objet as const quand elles ont aussi besoin des valeurs à l'exécution. Les unions sont entièrement effacées, fonctionnent avec le type stripping intégré de Node et l'option erasableSyntaxOnly, et acceptent des chaînes simples comme "active". Les enums conviennent aussi, surtout dans les bases de code qui les utilisent déjà.
Quelle est la différence entre enum et const enum ?
Un enum classique se compile en un objet que vous pouvez parcourir et consulter à l'exécution. Un const enum est supprimé à la compilation et chaque utilisation est remplacée par sa valeur (Size.Large devient 2) : il ne coûte rien à l'exécution, mais ne peut pas être parcouru, et les outils qui compilent un fichier à la fois en restreignent l'usage.