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.
enum | The Stage literal union from before | |
|---|---|---|
| Exists at runtime | Yes, as an object (unless const enum) | No — fully gone after compilation |
| Safe to reorder | No — a member's number can silently shift | There's nothing to reorder; nothing is numbered |
| Reads cleanly in raw JSON | Numbers are easy to misinterpret | Strings, like "screening", are self-explanatory |
| Where you'll actually see it | Older code, mostly | The 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.