TypeScript Enum vs Union Type: Which One Should You Use?

Problem

When defining a set of constants in TypeScript, you face the classic dilemma: enum or union type? Both work, but they have very different runtime behaviors.

The Enum Trap

enum Status {
  Active = 'ACTIVE',
  Inactive = 'INACTIVE',
  Pending = 'PENDING'
}

This compiles to a runtime object in JavaScript:

var Status;
(function (Status) {
  Status["Active"] = "ACTIVE";
  Status["Inactive"] = "INACTIVE";
  Status["Pending"] = "PENDING";
})(Status || (Status = {}));

That’s extra bundle size, and it doesn’t tree-shake well.

Solution: Union Type with as const

const STATUS = {
  Active: 'ACTIVE',
  Inactive: 'INACTIVE',
  Pending: 'PENDING',
} as const;

type Status = typeof STATUS[keyof typeof STATUS];
// Result: 'ACTIVE' | 'INACTIVE' | 'PENDING'

With as const, TypeScript infers literal types. Minimal runtime footprint, excellent tree-shaking.

For simple cases, a plain union is even cleaner:

type Direction = 'up' | 'down' | 'left' | 'right';

Key Points

  • enum creates a runtime object, increasing bundle size
  • const enum gets inlined but doesn’t work with --isolatedModules (Vite, Next.js)
  • as const + union type is the better default for most use cases
  • Use enum only when you need bitwise flags or runtime reverse mapping