An enum is a named set of constants. It is one of the few TypeScript features that generates code rather than being erased (Handbook: Enums).
Declaration
EnumDeclaration:
[declare] [const] enum Name { [EnumMember {, EnumMember} [,]] }
EnumMember:
PropertyName [= Expression]
A member name may be an identifier or a string, but not a number. A declaration may be repeated, in which case the members combine, and only the first declaration may omit the initialiser of its first member.
Numeric enums
A member with no initialiser takes the previous value plus one, and the first defaults
to 0. Values may be any number, including fractions or negatives.
enum Direction { Up = 1, Down, Left, Right } // Up=1, Down=2, Left=3, Right=4
enum Access {
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write, // refers to earlier members
Length = "abc".length, // computed
}
A member is constant if its initialiser is a constant enum expression: a
literal, a reference to an earlier constant member, or a parenthesised, unary
+ - ~ or binary + - * / % << >> >>> & | ^ combination of
these. Any other initialiser is computed. A member after a computed member must
have its own initialiser.
enum E {
A = "x".length,
B, // error: needs an initialiser after a computed member
}
String enums
Every member of a string enum needs an initialiser: a string literal, a reference to another string member, or since 5.0 a template or expression built from constants. Members of string enums have no reverse mapping, and a plain string is not assignable to one.
enum Level { Info = "info", Warn = "warn" }
const a: Level = "info"; // error
const b: string = Level.Info; // fine
const prefix = "p";
enum Tag { A = prefix, B = `${prefix}x` }
A single enum can mix number and string members. This is rarely useful.
Enums as types
An enum name is also a type. Since 5.0 every enum is a union of its members, and each
member is a type too. A numeric enum is assignable to and from number, but
assigning a literal outside the enum’s values is an error. Different enums are not
compatible with each other.
enum D { Up = 1, Down }
let d: D = 1; // fine
d = 7; // error: not a member
let n: number = D.Up; // fine
function f(x: D) {
if (x !== D.Up || x !== D.Down) {} // error: this condition is always true
}
type Names = keyof typeof D; // "Up" | "Down"
keyof typeof gives the member namesWhat an enum emits
An enum becomes a variable and a function that fills it. A numeric member also gets a reverse mapping from value to name.
enum Direction { Up, Down }
enum Text { A = "a" }
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
})(Direction || (Direction = {}));
var Text;
(function (Text) {
Text["A"] = "a";
})(Text || (Text = {}));
Direction[0] is "Up"Because enums generate code, they are rejected by
erasableSyntaxOnly.
const enums
A const enum is removed from the output, and each use is replaced by the
value. It may hold only constant members, and it cannot be used as a value on its own,
only through a member. An initialiser that evaluates to NaN or
Infinity is an error here, but not in an ordinary enum.
const enum Flag { A = 1, B = 2 }
const x = Flag.B;
const x = 2 /* Flag.B */;
The inlining means that a value compiled against one version of a library may disagree
with another version at run time, which is why the Handbook advises against publishing
const enums. preserveConstEnums makes the compiler write the enum as
well.
Ambient enums
declare enum describes an enum that exists elsewhere, and generates nothing.
In an ambient non-const enum, a member with no initialiser is treated as computed, unlike in
a normal enum.
declare enum Status { Ok = 200, NotFound = 404 }
Objects as enums
An object with as const
gives the same names with no TypeScript-only syntax at run time.
const Direction = { Up: 0, Down: 1 } as const;
type Direction = (typeof Direction)[keyof typeof Direction]; // 0 | 1