Enums
Learn TypeScript enums: numeric enums, string enums, const enums, and how they compare to literal union types.
Introduction
An enum names a fixed, related set of constant values — like the days of the week, or the status of an order. Unlike almost every other TypeScript type construct, a regular enum is not fully erased: it compiles into a real JavaScript object that exists at runtime.
- How numeric enums auto-increment and support reverse mapping.
- How string enums differ from numeric enums.
- What const enums are and their build-tool caveats.
- How enums compare to literal union types, and which to reach for.
- When to avoid enums in modern TypeScript codebases.
Numeric Enums
By default, enum members are assigned auto-incrementing numbers starting from 0. Numeric enums also support "reverse mapping" — looking up a member's name from its number.
enum Direction { Up, // 0 Down, // 1 Left, // 2 Right, // 3}
let move: Direction = Direction.Up;console.log(move); // 0console.log(Direction[0]); // "Up" (reverse mapping)String Enums
String enums require every member to have an explicit string value, and — unlike numeric enums — do not support reverse mapping. They are generally more debuggable, since logging a string enum value shows something meaningful instead of a bare number.
enum Status { Active = "ACTIVE", Inactive = "INACTIVE", Pending = "PENDING",}
function describe(status: Status): string { return `Order is ${status}`;}
console.log(describe(Status.Active));Click Run to see what this code prints.
Const Enums
Prefixing an enum with `const` tells the compiler to inline every usage directly at compile time, generating no runtime object at all — a small performance and bundle-size win. The catch: `const enum` requires whole-program type information, so it is not supported by single-file transpilers like Babel or esbuild running with `isolatedModules` enabled, which is common in modern build setups.
const enum LogLevel { Info, Warn, Error,}
// Compiles to: console.log(1);console.log(LogLevel.Warn);Enums vs Literal Unions
| Aspect | Enum | Literal Union |
|---|---|---|
| Exists at runtime | Yes (except const enum) | No — compile-time only |
| Bundle size impact | Adds a small runtime object | Zero — fully erased |
| Interop with plain JS values | Requires importing the enum | Any matching string/number works directly |
| Build tool compatibility | const enum can break single-file transpilers | Always compatible |
When Not to Use Enums
Because of the tradeoffs above, many modern TypeScript style guides — including large teams at companies with big TypeScript codebases — recommend literal union types over enums for simple, closed sets of string values, reserving actual enums for cases where you specifically want a real, iterable runtime object.
Common Mistakes
- Expecting reverse mapping to work on string enums — it only exists for numeric enums.
- Inserting a new member in the middle of a numeric enum, silently shifting every subsequent member's numeric value.
- Enabling `const enum` in a project built with a single-file transpiler (Babel, esbuild, SWC) under isolatedModules, causing build errors.
- Using an enum where a simple literal union type would have been simpler and added zero runtime cost.
Best Practices
- Prefer string enums over numeric enums when the value matters for logging or debugging.
- Consider literal union types instead of enums for simple, closed sets of string values, especially in modern esbuild/SWC/Babel-based build pipelines.
- Avoid `const enum` unless you fully control the build pipeline and know it's compatible.
- Never rely on the exact numeric value of a numeric enum member remaining stable across future refactors.
Frequently Asked Questions
Numeric enums auto-increment from an integer (0 by default) and support reverse mapping; string enums require an explicit string value per member and have no reverse mapping.
Yes, unlike almost every other TypeScript type construct — a regular enum compiles into a real JavaScript object. const enum is the exception, since it is inlined and produces no runtime object.
Not necessarily — many modern TypeScript style guides prefer literal union types for simple cases, reserving enums for when a real, iterable runtime object is genuinely useful.
Technically yes ("heterogeneous enums") but it is discouraged and rarely useful in real code.
Key Takeaways
- Numeric enums auto-increment from 0 by default and support reverse mapping.
- String enums require explicit values and are more debuggable, but have no reverse mapping.
- const enum inlines values at compile time with no runtime object, but can break some build tools.
- Literal union types are a zero-runtime-cost alternative to enums for simple, closed sets of values.
- Enums are one of the few TypeScript constructs that actually exist in the compiled JavaScript.
Summary
You now understand enums and how they compare to literal union types. Next, you'll learn type narrowing and type guards — how TypeScript proves a value's specific type from the runtime checks you already write.