learn
TypeScript

Enums, const assertions and literal unions

When an enum earns its keep, and when a union of strings wins.

"New, paid, shipped, done" — a fixed set of names. TypeScript offers two ways to model it, and interviewers want the trade-off, not just the syntax.

Real-life situation

An order tracker stores its status in a database column and renders it in a dropdown. A string union ("new" | "paid" | ...) erases completely and matches the stored text exactly. A numeric enum emits a real lookup object — handy when you need reverse mapping, but a second source of truth to keep in sync with the database.

Visual

Enum versus string union
BrowserAPIRealtimeCacheDatabaseShareddurableephemeral

Try it

Order statuses, two spellings

Checked with TypeScript 6.0.3, strict mode

typescriptTab moves to the next control
type OrderStatus = "new" | "paid" | "shipped" | "done";
function next(status: OrderStatus): OrderStatus {
if (status === "new") {
  return "paid";
}
if (status === "paid") {
  return "shipped";
}
return "done";
}
enum OldStatus {
New = 0,
Paid = 1,
Shipped = 2,
}
console.log(next("new"));
console.log(next("paid"));
console.log(OldStatus.Paid);
console.log(next("refunded"));

Compiler not loaded. Check or Run loads it here (about 9 MB) — nothing downloads until you do.

Runner helpers

sleep(ms) waits; fakeFetch(url) returns deterministic sample data. TypeScript is stripped before running; imports and exports are not available in the sandbox.

Try changing "refunded" to "shipped" — clean, printing "done". Notice OldStatus.Paid prints 1: numeric enums exist at runtime, while the union vanished entirely. as const objects give a middle path — real values plus literal types — without enum quirks.

Default stance: reach for the string union. Take the enum when you need runtime values (numeric codes, reverse lookups) and accept the emitted object. const enum avoids the object but complicates isolated builds — say that sentence in interviews and move on.

Will it compile? — decide first, then reveal.

check.ts
enum Direction {
Up = 0,
Down = 1,
}
const d: Direction = Direction.Up;
console.log(d);

Interview questions

What is a literal type?basic

A type that allows exactly one value, like 'save' or 200. You use literals to name the precise strings and numbers your code accepts instead of widening to string or number. Combined with unions they describe finite choices — 'small' | 'medium' | 'large' — and misspellings become errors. Prefer literals for options, states, and tags where only specific values are valid.

Interviewers also ask

  • How do literals combine into a union of choices?
What is an enum in TypeScript?basic

A named set of related constants, like enum Role { Admin, User }, that groups choices under one type. Members are accessible as Role.Admin and document intent better than bare strings scattered through code. String enums assign explicit values per member; numeric enums auto-increment when you omit values. Use enums when the set belongs together and deserves a single importable name.

Interviewers also ask

  • How do numeric and string enums differ?
Should you use an enum or a string union?intermediate

Prefer a string union like 'admin' | 'user' for most application code — it is erasable, interoperates with plain JavaScript, and needs no import at runtime. Choose an enum when you want a namespaced container with behavior or when a library already exposes one. Unions also narrow cleanly in discriminated patterns. As a default, reach for the union and reserve enums for cases where the grouping itself adds value.

Interviewers also ask

  • Why do unions interoperate better with plain JS?
How do string enums work?basic

You assign each member an explicit string, like enum Direction { Up = 'UP', Down = 'DOWN' }, and use Direction.Up throughout your code. The explicit values serialize predictably and read well in logs. Unlike numeric enums, string enums have no reverse mapping surprises. They still emit a runtime object, so if you want zero runtime cost, consider a string union plus a plain object instead.

Interviewers also ask

  • Do string enums emit runtime code?
What is a const enum, and when should you avoid it?intermediate

A const enum is inlined at compile time, so uses of its members become raw values with no runtime object emitted. That saves bytes but breaks isolated transpilation workflows that compile file-by-file without type information. For that reason many style guides avoid const enum in shared libraries. Prefer regular enums or string unions unless you control the whole build and genuinely need the inlining.

Interviewers also ask

  • Why do some bundlers struggle with const enums?
How does as const create literal types?intermediate

Appending as const to an object or array freezes it to its narrowest literal form: properties become readonly and values become literals instead of widening to string or number. A config like { mode: 'dark' } as const gives mode the type 'dark', so typos elsewhere fail. Use it for constant tables, option maps, and test fixtures where the exact values are the contract. It is the lightest way to get enum-like precision.

Interviewers also ask

  • What two things does as const change about a literal?

Key takeaways

  • String unions erase fully and match stored text — the default choice.
  • Numeric enums emit a runtime object; that is the whole cost.
  • as const gives values plus literal types without enum machinery.

References

On this page