Are TypeScript Enums a Good Idea? (Bundle Size & Runtime)
Are TypeScript Enums a Good Idea?
Short answer: usually no, especially in frontend apps where bundle size and runtime behavior matter.
TypeScript enums look convenient, but they come with hidden runtime costs. In most cases, there are better zero-cost alternatives.
๐ง What Actually Happens with enum
Numeric enum
code
enum Status {
Idle,
Loading,
Success,
}
Generated JavaScript:
code
var Status;
(function (Status) {
Status[Status["Idle"] = 0] = "Idle";
Status[Status["Loading"] = 1] = "Loading";
Status[Status["Success"] = 2] = "Success";
})(Status || (Status = {}));
๐ This means:
- A real object exists at runtime
- Extra bytes in your JS bundle
- Worse tree-shaking than you expect
String enum
code
enum Mode {
Light = "light",
Dark = "dark",
}
Still generates a runtime object:
code
var Mode = {
Light: "light",
Dark: "dark",
};
Better than numeric enums, but still not free.
โ ๏ธ Why Enums Can Be a Problem
- โ Increase bundle size
- โ Add runtime objects
- โ Harder to tree-shake
- โ Easy to misuse for simple unions
Enums are not erased by TypeScript.
โก What About const enum?
code
const enum Status {
Idle,
Loading,
}
- โ Inlined at compile time
- โ Zero runtime cost
- โ Often incompatible with Babel / SWC / isolatedModules
- โ Risky in monorepos or mixed build setups
โ ๏ธ Use only if you fully control your toolchain.
โ Better Alternatives (Recommended)
1๏ธโฃ String Literal Unions (Best Default)
code
type Mode = "light" | "dark";
- โ Zero runtime cost
- โ Perfect for props, state, API types
- โ No runtime list (by default)
Pair with a list if needed:
code
export const MODES = ["light", "dark"] as const;
export type Mode = (typeof MODES)[number];
2๏ธโฃ as const Object Pattern (Enum-like DX)
code
export const Mode = {
Light: "light",
Dark: "dark",
} as const;
export type Mode = typeof Mode[keyof typeof Mode];
- โ Small runtime footprint
- โ Tree-shakable
- โ
Nice DX:
Mode.Dark - โ Works well with runtime validation
3๏ธโฃ Schema-first (APIs, forms)
code
const ROLES = ["admin", "user"] as const;
type Role = (typeof ROLES)[number];
Great when used with Zod / Valibot for runtime validation.
๐ง When Enums Are Actually OK
Use real enum if:
- You need runtime iteration
- You rely on reverse mapping
- You integrate with legacy code
- Bundle size is not critical
Enums are not evil โ just often overused.
๐ Comparison Table
| Pattern | Runtime cost | Tree-shaking | DX | Recommended |
|---|---|---|---|---|
| enum | โ | โ | โ | โ ๏ธ |
| const enum | โ | โ | โ ๏ธ | โ ๏ธ |
| union type | โ | โ | โ | โญ |
| as const object | โ | โ | โ | โญโญ |
โ Final Recommendation
Prefer unions and
as constobjects in frontend TypeScript.
Reach forenumonly when you truly need runtime behavior.
This approach gives you:
- Smaller bundles
- Faster startup
- Clearer intent
- Fewer build surprises
How did you like this post?
๐0
โค๏ธ0
๐ฅ0
๐ค0
๐ฎ0