TypeScript Enums
A reference guide to TypeScript numeric enums, string enums, reverse mappings, and compile-time constants.
TL;DR
- Define enumerated sets using the
enumkeyword declaration. - Prefer string
enummembers for readable debugging outputs and API serialization. - Use
const enumdeclarations to inline values directly into emitted code.
String Enums
string enum declarationBinds each member explicitly to an exact string value.
enum Status {
Pending = 'pending',
Active = 'active',
Rejected = 'rejected'
}switch statement checkBranches through members with compile-time type safety.
function getBadgeColor(s: Status): string {
switch (s) {
case Status.Active: return 'green';
case Status.Pending: return 'yellow';
case Status.Rejected: return 'red';
}
}Object.values iterationLists all assigned runtime values of a string enum.
const statuses: string[] = Object.values(Status);
console.log(statuses); // ['pending', 'active', ...]Numeric Enums
auto-incrementing membersInitializes members sequentially starting from zero.
enum Direction {
North, // 0
East, // 1
South, // 2
West // 3
}custom initial valueStarts numbering from an explicit numeric base.
enum HttpCode {
Ok = 200,
Created = 201,
Accepted = 202
}bitwise flag enumCombines binary permission masks using bitwise shifts.
enum Permission {
Read = 1 << 0, // 1
Write = 1 << 1, // 2
Exec = 1 << 2 // 4
}
const flags = Permission.Read | Permission.Write;Reverse Mapping
lookup by valueRetrieves the string member name using its numeric index.
enum Level { Low = 1, High = 2 }
const label: string = Level[2]; // 'High'iteration pitfallExposes dual keys when inspecting numeric enums directly.
enum Mode { Dev, Prod }
// Object.keys(Mode) yields: ['0', '1', 'Dev', 'Prod']filtering reverse keysRemoves numeric indices when enumerating member names.
const names = Object.keys(Mode).filter(
key => isNaN(Number(key))
);
console.log(names); // ['Dev', 'Prod']Const Enums
const enum declarationInlines member literals directly into calling sites.
const enum Priority {
Low = 10,
High = 50
}
const taskPriority = Priority.High; // emits 50zero runtime footprintEmits no JavaScript lookup objects during compilation.
// TypeScript generates no runtime object for PrioritypreserveConstEnums optionEmits runtime objects alongside inlining for debugging.
// Set in tsconfig.json
{
"compilerOptions": {
"preserveConstEnums": true
}
}Object as Const Alternative
as const object patternUses standard JavaScript objects with literal assertions.
const LogLevel = {
Info: 'info',
Warn: 'warn',
Error: 'error'
} as const;derived union typeExtracts union types directly from object properties.
type Keys = keyof typeof LogLevel;
type LogLevel = (typeof LogLevel)[Keys];
// 'info' | 'warn' | 'error'idiomatic alternativeAvoids TypeScript-specific runtime syntax extensions.
function log(level: LogLevel, msg: string) {
console.log(`[${level.toUpperCase()}]: ${msg}`);
}Tips
- Explicitly initialize the first numeric
enummember with a positive number to avoid treating zero as falsy. - Extract all string values from an enum at runtime using the standard
Object.values()helper function.
Warnings
- Numeric enums generate reverse mapping keys that appear unexpectedly during iterations over
Object.keys()in runtime JavaScript code. - Avoid using
const enumdeclarations when building shared packages consumed by tools withisolatedModulesenabled.
In Practice
Demonstrates state validation, string serialization, and transition guards using typed string enums.
- Declare a string enum holding ticket workflow progression states.
- Implement a state machine function checking valid state transitions.
- Execute transitions and log current state values.
- Verify that invalid transitions are rejected predictably at runtime.
enum Status {
Draft = 'DRAFT',
Review = 'REVIEW',
Done = 'DONE'
}
const NEXT: Record<Status, Status[]> = {
[Status.Draft]: [Status.Review],
[Status.Review]: [Status.Done],
[Status.Done]: []
};
function canTransition(from: Status, to: Status) {
return NEXT[from].includes(to);
}
const ok = canTransition(Status.Draft, Status.Review);
console.log(`Allowed: ${ok}`);FAQ
String enums are strongly preferred when enum values are serialized into JSON, logged to observability platforms, or sent across network requests. Because their runtime values are meaningful strings like 'active', they make debugging much simpler than numeric values like 0.
The compiler creates a two-way lookup table for numeric enums, mapping names to numbers and numbers back to names. This allows lookups like Status[1], but it also causes Object.keys() to return both keys and values unless manually filtered.
enum Direction { Up, Down }
Direction[0]; // 'Up'A const enum is erased at compile time by the TypeScript compiler and replaced with literal numbers or strings. Transpilers like esbuild and Babel compile one file at a time without reading imported enum definitions, causing missing symbol errors under isolatedModules.
Cast the enum values to an array using Object.values(MyEnum) and check membership with the includes() method. For full type narrowing, wrap this evaluation inside a custom type guard function returning val is MyEnum.