DEV Community

Mohsen Fallahnejad
Mohsen Fallahnejad

Posted on Edited on Originally published at bitlyst.blog

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

enum Status {
  Idle,
  Loading,
  Success,
}
Enter fullscreen mode Exit fullscreen mode

Generated JavaScript:

var Status;
(function (Status) {
  Status[Status["Idle"] = 0] = "Idle";
  Status[Status["Loading"] = 1] = "Loading";
  Status[Status["Success"] = 2] = "Success";
})(Status || (Status = {}));
Enter fullscreen mode Exit fullscreen mode

👉 This means:

  • A real object exists at runtime
  • Extra bytes in your JS bundle
  • Worse tree-shaking than you expect

String enum

enum Mode {
  Light = "light",
  Dark = "dark",
}
Enter fullscreen mode Exit fullscreen mode

Still generates a runtime object:

var Mode = {
  Light: "light",
  Dark: "dark",
};
Enter fullscreen mode Exit fullscreen mode

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?

const enum Status {
  Idle,
  Loading,
}
Enter fullscreen mode Exit fullscreen mode
  • ✅ 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)

type Mode = "light" | "dark";
Enter fullscreen mode Exit fullscreen mode
  • ✅ Zero runtime cost
  • ✅ Perfect for props, state, API types
  • ❌ No runtime list (by default)

Pair with a list if needed:

export const MODES = ["light", "dark"] as const;
export type Mode = (typeof MODES)[number];
Enter fullscreen mode Exit fullscreen mode

2️⃣ as const Object Pattern (Enum-like DX)

export const Mode = {
  Light: "light",
  Dark: "dark",
} as const;

export type Mode = typeof Mode[keyof typeof Mode];
Enter fullscreen mode Exit fullscreen mode
  • ✅ Small runtime footprint
  • ✅ Tree-shakable
  • ✅ Nice DX: Mode.Dark
  • ✅ Works well with runtime validation

3️⃣ Schema-first (APIs, forms)

const ROLES = ["admin", "user"] as const;
type Role = (typeof ROLES)[number];
Enter fullscreen mode Exit fullscreen mode

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 const objects in frontend TypeScript.

Reach for enum only when you truly need runtime behavior.

This approach gives you:

  • Smaller bundles
  • Faster startup
  • Clearer intent
  • Fewer build surprises

Originally published on Bitlyst.

Top comments (0)