Enums

An enum is a named set of constants — a way to say "this value must be one of these few options" and give each option a readable name. Unlike most TypeScript features, enums also produce real JavaScript code at runtime.

Numeric enums

TS status.ts
enum OrderStatus {
  Pending,
  Shipped,
  Delivered,
}

const status: OrderStatus = OrderStatus.Shipped;
console.log(status);
console.log(OrderStatus[status]);
Output
1
Shipped

Members of a numeric enum are numbered automatically starting from 0, so Shipped is 1. The compiled object also contains a reverse mapping, which is why OrderStatus[1] gives back the name "Shipped".

You can set the starting value, or any individual value. Members after an explicit value continue counting from it:

TS http.ts
enum HttpCode {
  OK = 200,
  Created,
  BadRequest = 400,
  Unauthorized,
}

console.log(HttpCode.Created, HttpCode.Unauthorized);
Output
201 401

String enums

Each member of a string enum must be given an explicit string value:

TS direction.ts
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

function move(d: Direction) {
  console.log(`moving ${d}`);
}

move(Direction.Left);
Output
moving LEFT

String enums are easier to debug — a log line says LEFT rather than a mysterious 2 — but they don't get a reverse mapping.

Enums are nominal

Unlike almost everything else in TypeScript, a string enum is checked by name, not by shape. A raw string with the same value is rejected:

TS nominal.ts
enum Direction {
  Up = "UP",
  Down = "DOWN",
}

function move(d: Direction) {}

move("UP");
tsc output
error TS2345: Argument of type '"UP"' is not assignable to parameter of type 'Direction'.

Callers must write Direction.Up. Whether that's a feature (consistency) or a nuisance (extra imports) is a matter of taste.

What an enum compiles to

JS status.js (compiled)
var OrderStatus;
(function (OrderStatus) {
    OrderStatus[OrderStatus["Pending"] = 0] = "Pending";
    OrderStatus[OrderStatus["Shipped"] = 1] = "Shipped";
    OrderStatus[OrderStatus["Delivered"] = 2] = "Delivered";
})(OrderStatus || (OrderStatus = {}));

That runtime object means you can loop over an enum or look values up dynamically. It also means enums are one of the few TypeScript features that aren't simply erased, which matters for tools that strip types without compiling (see lesson 44).

const enums

A const enum produces no object at all — every use is replaced with the literal value at compile time:

TS const-enum.ts
const enum Size {
  Small = "S",
  Large = "L",
}

const chosen = Size.Large;
console.log(chosen);
Output
L

The compiled line is simply const chosen = "L". It saves a few bytes, but const enums don't work well with tools that compile one file at a time, so many projects avoid them.

The common alternative: literal unions

Many TypeScript developers skip enums and use a union of string literals instead (lesson 11). It needs no runtime code and accepts plain strings:

TS union-alternative.ts
type Direction = "UP" | "DOWN" | "LEFT" | "RIGHT";

function move(d: Direction) {
  console.log(`moving ${d}`);
}

move("UP");
Output
moving UP

If you also need the list of values at runtime, an object with as const gives you both (lesson 14). Enums are perfectly valid — you'll see them in Angular and many backend codebases — but it's worth knowing the alternatives.

Try it yourself

Try it yourself
Console output