const enum
Declares an enum whose members are inlined at each use site, emitting no runtime object.
const enum Name {
Member,
Initialized = 1,
Computed = Member | Initialized // constant enum expressions only
}
// Ambient form, for a .d.ts describing an enum that exists elsewhere:
declare const enum Name { Member = 1 }- Added in TypeScript 1.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$Name | identifier | Creates a type, but no value survives compilation — there is no object to reference at runtime. |
$Member | identifier | A member name. Every reference to it is replaced in the output by its literal value plus a comment. |
$initializeroptional | constant enum expression= previous member + 1, or 0 for the first member | Optional. Must be a constant expression the compiler can fold — computed members that need runtime evaluation are not allowed in a const enum. |
Errors
Errors at compile time on any member whose initializer is not a constant enum expression, on an index access such as Direction[0] because there is no reverse mapping, and on ambient const enums when isolatedModules is enabled.
Changelog
- PHP 4.0preserveConstEnums defaults to true when isolatedModules is set, so the enum object is emitted after all and inlining stops being a silent transpiler hazard.
- PHP 5.0Non-ambient const enums became usable under isolatedModules; ambient const enums declared in .d.ts files remain unsupported there.
- PHP 5.8erasableSyntaxOnly rejects const enum declarations along with all other enums, since even an inlined enum is TypeScript-specific syntax rather than a type annotation.
A const enum is a regular enum that the compiler folds away. Every reference becomes the literal value, and no object is emitted, so nothing remains to import, iterate or reverse-map.
What the output looks like
[Direction.Up, Direction.Down] compiles to [0 /* Direction.Up */, 1 /* Direction.Down */]. The comment is the only trace.
When to reach for it
Rarely. It is worth it in genuinely hot code inside a single project, where the enum is private to that project and the object lookup is measurable. For everything else the saving is negligible and the constraints are real.
What trips people up
Inlining is a whole-program transform, so it fights every tool that compiles one file at a time — Babel, esbuild, swc, sucrase, ts-loader in transpile-only mode. Those tools cannot see the enum's values from another file, so isolatedModules exists to warn you. Worse is the versioning hazard the handbook calls out: if you inline the values from version A of a library and ship against version B where a member's value changed, the compiled constant is silently wrong. Members are also unavailable at runtime entirely — no Object.keys, no Direction[0]. If you publish a library, do not export ambient const enums; use preserveConstEnums and strip const from the emitted declarations instead.
Example
const enum Direction {
Up,
Down,
Left,
Right,
}
const path: Direction[] = [Direction.Up, Direction.Right, Direction.Down];
// The emitted JavaScript contains no Direction object at all:
// const path = [0 /* Direction.Up */, 3 /* Direction.Right */, 1 /* Direction.Down */];
console.log(path);
// [ 0, 3, 1 ]
// Direction[0] would be a compile error — a const enum has no reverse mapping,
// and there is no object left to index into.When to use it
- Fold protocol opcodes into literal numbers inside a tight parsing loop in a single project.
- Keep a large table of internal constants out of the shipped bundle entirely.
- Diagnose an 'ambient const enums are not supported' error after switching a build to esbuild or swc.
- Convert a library's exported const enums to plain enums before publishing.
Frequently asked questions
What is the difference between enum and const enum?
A regular enum emits an object at runtime, while a const enum is erased and every member reference is replaced by its literal value. That means a const enum has no reverse mapping, cannot be iterated with Object.keys, and cannot be passed around as a value.
Why does const enum fail with isolatedModules?
Inlining requires the compiler to read the enum's values from wherever it is declared, which a single-file transpiler cannot do. Since TypeScript 5.0, const enums declared in your own source are allowed under isolatedModules; ambient const enums in .d.ts files still are not, because their values only exist in a type declaration.
Should I use const enum for performance?
Almost never — the saving is one property lookup, which is irrelevant outside genuinely hot loops, and the cost is incompatibility with every fast transpiler plus a real risk of inlining stale values from a dependency. Use a plain enum or an `as const` object unless profiling has actually pointed here.
More examples
Publishing safely with preserveConstEnums
This is the pattern the handbook recommends for library authors. preserveConstEnums keeps the real object in the JavaScript output while still allowing your own code to inline, and the build step then rewrites `const enum` to `enum` in the shipped .d.ts so downstream consumers using isolatedModules are never asked to inline values they cannot see.
// src/codes.ts
export const enum StatusCode {
Ok = 200,
NotFound = 404,
}
export function label(code: StatusCode): string {
return code === StatusCode.Ok ? "ok" : "not found";
}
// tsconfig.json
// { "compilerOptions": { "preserveConstEnums": true } }
//
// Without the flag, dist/codes.js contains only:
// return code === 200 /* StatusCode.Ok */ ? "ok" : "not found";
// With the flag, the StatusCode object is emitted as well, so a consumer
// whose bundler cannot inline still resolves StatusCode.Ok at runtime.
console.log(label(StatusCode.NotFound));
// not foundWhy a single-file transpiler cannot handle it
The declaration file carries the value 1, but a per-file transpiler never loads it, so it has nothing to substitute. This is exactly the situation isolatedModules is designed to flag before it becomes a runtime bug, and it is why ambient const enums are the one const-enum form that remains banned under that option.
// flags.d.ts — an ambient const enum from a dependency
declare const enum Flags {
None = 0,
Verbose = 1,
}
// app.ts
function run(flags: Flags): string {
return flags === Flags.Verbose ? "verbose" : "quiet";
}
// tsc sees flags.d.ts and emits: flags === 1 /* Flags.Verbose */
// esbuild/babel/swc see app.ts alone, cannot resolve Flags.Verbose,
// and with isolatedModules tsc reports:
// Cannot access ambient const enums when 'isolatedModules' is enabled.
console.log(run(1 as Flags));
// verbose
Discussion