How do enums work in TypeScript?

Enums define a set of named constants. TypeScript supports numeric and string enums - but const enums and union types are often better alternatives.

TypeScript

Overview

An `enum` defines a named set of constants, making intent clear and preventing magic values. TypeScript has two main flavors: **numeric** (auto-incremented integers) and **string** (explicit string values). Both compile to JavaScript objects, which has tradeoffs.

Key Points

  • •Numeric enum: values are 0, 1, 2… by default. You can set a starting value.
  • •String enum: each member must be explicitly initialised with a string literal.
  • •`const enum` is inlined at compile time (no runtime object generated) - more efficient.
  • •A popular alternative: a union of string literals (`type Direction = "Up" | "Down"`) - no compiled output.
  • •Enums are one of the few TypeScript features that emit runtime JavaScript code; string literal unions do not.

Examples

Numeric enum

typescript
enum Direction {
  Up,    // 0
  Down,  // 1
  Left,  // 2
  Right, // 3
}

console.log(Direction.Up);    // 0
console.log(Direction[0]);    // 'Up' (reverse mapping)

function move(d: Direction) { ... }
move(Direction.Left);

String enum (recommended)

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

const userStatus: Status = Status.Active;
console.log(userStatus);  // 'ACTIVE'

// String enums have no reverse mapping, which avoids some pitfalls

const enum (zero runtime cost)

typescript
const enum HttpMethod {
  Get    = 'GET',
  Post   = 'POST',
  Put    = 'PUT',
  Delete = 'DELETE',
}

// TypeScript inlines the value at every usage - no JS object emitted
const method = HttpMethod.Get;
// Compiled to: const method = 'GET';

const enums cannot be used across module boundaries with isolatedModules.

Union type alternative (often preferred)

typescript
// No runtime cost, simpler, works everywhere
type Direction = 'Up' | 'Down' | 'Left' | 'Right';
type Status    = 'active' | 'inactive' | 'pending';

function move(dir: Direction) { ... }
move('Up'); // works directly, no Direction.Up needed

Pro Tip

In modern TypeScript, prefer string literal union types over enums for most cases: `type Status = "active" | "inactive"`. They have zero runtime cost, are easier to use, and integrate better with JSON APIs. Use enums when you need an associated value (like an HTTP status code number) or want to iterate over the members at runtime.