Skip to main content
CodeOath
← All posts

TypeScript63 min total · 21 parts

TypeScript Fundamentals: Types, Interfaces, Generics, and Why It Catches Bugs Before Runtime

Part 9 of 21 · ~2 min

Enums vs. Union Literals

Before literal unions like Stage were the obvious default, TypeScript's answer to "a fixed set of named options" was enum, and it still shows up in real code — the queue's very first version used one:

enum StageEnum {
  Screening,
  Interview,
  Offer,
  Hired,
  Rejected,
}

function advance(stage: StageEnum) { /* ... */ }
advance(StageEnum.Interview);

An enum isn't erased the way almost everything else covered so far is — it compiles down to an actual object that exists at runtime, mapping names to numbers (0, 1, 2...) unless you assign values yourself. That numbering is exactly where it gets dangerous: insert Waitlisted between Interview and Offer, and every applicant previously stored as 2 (Offer) is now silently 3 (Hired) as far as any code — or any stored data — still reading the raw number is concerned, with nothing at the point of the change looking wrong. A const enum avoids emitting that runtime object at all, by inlining each member's value wherever it's used — with a real caveat: tools that compile one file at a time, like Babel's TypeScript preset, can't see across files to know what to inline, so const enum often can't be used at all in that kind of build setup.

enumThe Stage literal union from before
Exists at runtimeYes, as an object (unless const enum)No — fully gone after compilation
Safe to reorderNo — a member's number can silently shiftThere's nothing to reorder; nothing is numbered
Reads cleanly in raw JSONNumbers are easy to misinterpretStrings, like "screening", are self-explanatory
Where you'll actually see itOlder code, mostlyThe default for anything written today

StageEnum in the queue's codebase eventually became the Stage union from the previous chapter, for precisely the reordering reason above — a stage got inserted, a batch of applicants stored as the raw number 3 all became a different stage overnight, and nobody had touched their records directly. Current guidance, including from the TypeScript team itself, leans toward literal unions for new code; enum earns its keep mainly when you actually want that runtime object to exist — most commonly, to iterate over every possible value.