Master tagged discriminated unions to model state machines cleanly and enable zero-overhead compile-time type narrowing.
Discover how TypeScript infers types automatically and how let versus const declarations shape wide types versus narrow literal types.
A Discriminated Union (also called a Tagged Union or Algebraic Data Type) is a union of object shapes that share a single common literal property (the "discriminator" tag).
They allow TypeScript to narrow complex object shapes instantly in switch statements or if blocks with 100% type safety.
Define a common literal property (e.g. kind or status) across all union members:
type NetworkState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: string[] }
| { status: "error"; error: Error };
function renderUI(state: NetworkState): string {
// Narrowing occurs automatically based on the 'status' tag!
switch (state.status) {
case "idle":
return "Ready to load.";
case "loading":
return "Loading data...";
case "success":
return `Loaded ${state.data.length} items.`; // Safe to access state.data!
case "error":
return `Error: ${state.error.message}`; // Safe to access state.error!
}
}
[ NetworkState Union ]
├── status: "idle"
├── status: "loading"
├── status: "success" (data: string[]) <-- Narrowed via case "success"!
└── status: "error" (error: Error)
kind on one member and type on another prevents TypeScript from identifying the discriminator tag. Keep the discriminator property name identical across all members!Use discriminated unions for Redux actions, API state machines, and AST nodes. Next, let's explore Built-in Utility Types: Partial, Required, Readonly, Pick, and Omit!