← Back to TypeScript Course | Chapter 19: Enums Deep Dive | Lesson 5 of 6

Enum vs Union Types

Enums create named values with a runtime representation, while union types describe a set of allowed values at the type level. For many modern TypeScript applications, literal unions are simpler when no runtime enum object is needed.
Syntax
typescript
enum EnumName { A = "a", B = "b" }
type UnionName = "a" | "b";

Enum and Union Basics

Both enums and union types can restrict a value to a known set of options, but they work fundamentally differently once you get past the type-checking stage and into actual runtime behavior.

Example: Enum and Union Basics

typescript
enum StatusEnum { Pending, Done }
type StatusUnion = "pending" | "done";
const a: StatusEnum = StatusEnum.Pending;
const b: StatusUnion = "pending";
console.log(a, b);

Runtime Differences

A normal enum emits a real JavaScript object at runtime that you could inspect or log, while a union type is a purely compile-time construct that disappears entirely once TypeScript compiles down to JavaScript.

Example: Runtime Differences

typescript
enum StatusEnum { Pending, Done }
console.log(StatusEnum); // a real object exists at runtime
type StatusUnion = "pending" | "done";
const b: StatusUnion = "pending"; // no runtime trace of the type itself
console.log(b);

Function Parameters

Both approaches restrict function arguments to a known set of values equally well at the type-checking level — the difference only shows up in what, if anything, exists once the code actually runs.

Example: Function Parameters

typescript
enum StatusEnum { Pending, Done }
type StatusUnion = "pending" | "done";
function handleEnum(s: StatusEnum) { return s; }
function handleUnion(s: StatusUnion) { return s; }
console.log(handleEnum(StatusEnum.Done), handleUnion("done"));

When Unions Are Simpler

A literal union (like "pending" | "done" | "failed") is often the simpler choice for a small, fixed set of string values when you don't need a runtime object or any enum-specific behavior like reverse mapping.

Example: When Unions Are Simpler

typescript
type Status = "pending" | "done" | "failed";
function label(status: Status) {
  return `Status: ${status}`;
}
console.log(label("pending"));

When Enums Are Useful

Enums earn their keep when you need named members backed by a shared runtime object, or when numeric values genuinely matter to how the application behaves — situations a union type alone can't provide.

Example: When Enums Are Useful

typescript
enum HttpStatus {
  Ok = 200,
  NotFound = 404,
}
function isSuccess(status: HttpStatus) {
  return status === HttpStatus.Ok;
}
console.log(isSuccess(HttpStatus.Ok));
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Expecting a union type to exist at runtime, when it is erased and cannot be iterated.
  2. Assigning a plain string like "pending" to a string enum type, which is not allowed.
  3. Choosing an enum when a simple union would do, adding extra JavaScript output for no benefit.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.