thecodex.expert · The Codex Family of Knowledge
TypeScript

Enums

A regular numeric enum will silently accept a number you never declared as a valid member — a real gap in type safety that the handbook itself documents.

TypeScript 6.0 Handbook favors unions now Last verified:
Canonical Definition

enum declares a set of named constants — numeric (auto-incrementing from 0 by default) or string (each member needs an explicit value). Unlike most TypeScript constructs, a regular enum is not erased at compile time: it generates an actual JavaScript object at runtime, unless declared const enum, which inlines the values with zero runtime object. This is one of the few areas where the official TypeScript Handbook itself documents genuine drawbacks — including that a numeric enum's type unsafely accepts any number — and now points most new code toward literal-type unions instead.

Numeric and string enums

Numeric enums support reverse mapping (going from the value back to the member name); string enums do not, since that mapping would be ambiguous with string values.

TypeScriptenums_basic.ts
enum Direction {
    Up,     // 0
    Down,   // 1
    Left,   // 2
    Right,  // 3
}

console.log(Direction.Up);        // 0
console.log(Direction[0]);        // "Up" — reverse mapping, numeric enums only

enum Status {
    Pending = "PENDING",
    Active = "ACTIVE",
}
console.log(Status.Active);       // "ACTIVE"

The real type-safety gap in numeric enums

This is documented in the official handbook itself as a genuine drawback — not a myth or an exaggeration — because bitmask-style enum usage legitimately relies on arbitrary numeric values, so TypeScript can't universally reject this without breaking that pattern.

TypeScriptenum_gap.ts
enum Direction { Up, Down, Left, Right }

function move(direction: Direction) { /* ... */ }

move(Direction.Up);   // fine
move(99);              // ALSO type-checks — no error, even though 99 isn't a real member

Why the handbook now favors union types

A union of string literals is completely erased at compile time (zero runtime bytes, unlike a regular enum's generated object), doesn't have this type-safety gap, and needs no import to reference — which is why the official TypeScript documentation itself now points most new code toward this pattern instead of enum, reserving enums mainly for codebases that already use them extensively or specifically need reverse mapping.

TypeScriptunion_alternative.ts
type Direction = "up" | "down" | "left" | "right";   // the now-preferred alternative

function move(direction: Direction) { /* ... */ }
move("up");        // fine
// move("diagonal"); // COMPILE ERROR — this one IS properly rejected

Sources

1
Microsoft. TypeScript Handbook, "Enums" (including its documented caveats section), typescriptlang.org/docs/handbook/enums.html.