Suggest an editImprove this articleRefine the answer for “What is a "discriminated union"?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)A **discriminated union** is a `union` of objects that share a common tag property (the discriminant), whose value uniquely determines the object's type. **Key point:** thanks to the discriminant, TypeScript automatically narrows the type in an `if` or `switch`, without needing `as` or manual property checks.Shown above the full answer for quick recall.Answer (EN)Image## What is a discriminated union? A **discriminated union** is a `union` of objects that share a **common tag property** (called the *discriminant* or *tag*), whose value uniquely **determines the object's type**. This lets TypeScript **recognize** which exact variant of the type is being used, and **automatically narrow the type (type narrowing)** during checks. --- ### A simple example ```javascript type Circle = { kind: "circle"; radius: number; }; type Square = { kind: "square"; side: number; }; type Shape = Circle | Square; ``` Here: - `Shape` is a union (`Circle | Square`); - the `kind` field is the **discriminant**, it tells the variants apart. --- ### Usage with type narrowing ```javascript function area(shape: Shape): number { if (shape.kind === "circle") { // here shape: Circle return Math.PI * shape.radius ** 2; } else { // here shape: Square return shape.side ** 2; } } ``` TypeScript **automatically determines** that in each `if`/`else` block the type has narrowed to the correct variant (`Circle` or `Square`). --- ## How this works TypeScript sees: 1. a union (`Circle | Square`), 2. a common property (`kind`), 3. and unique string literals in that property. After the check `shape.kind === "circle"`, the compiler knows that in this context `shape` **is definitely Circle**. --- ## An example with several variants ```javascript type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function getArea(shape: Shape) { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; case "triangle": return (shape.base * shape.height) / 2; default: const _exhaustive: never = shape; // an exhaustiveness check return _exhaustive; } } ``` Here: - `switch` automatically narrows the type by `kind`. - `never` in `default` guarantees that all variants are handled. --- ## Advantages of a discriminated union **1. Safe branching** - TS guarantees that you have handled all cases. **2. No** `as` **and no casts** - the type is determined automatically. **3. Works great with** `switch`**,** `if`**, and pattern-like checks.** **4. Well suited for describing states, actions, errors, and events.** --- ## Real-world examples ### 1. Loading states (state machine pattern) ```javascript type LoadingState = { status: "loading" }; type SuccessState = { status: "success"; data: string }; type ErrorState = { status: "error"; error: Error }; type RequestState = LoadingState | SuccessState | ErrorState; function render(state: RequestState) { switch (state.status) { case "loading": return "Loading..."; case "success": return `Data: ${state.data}`; case "error": return `Error: ${state.error.message}`; } } ``` --- ### 2. Redux-like actions ```javascript type Action = | { type: "add"; payload: number } | { type: "remove"; id: string } | { type: "reset" }; function reducer(state: number[], action: Action) { switch (action.type) { case "add": return [...state, action.payload]; case "remove": return state.filter((_, i) => i.toString() !== action.id); case "reset": return []; } } ``` --- ### 3. API responses ```javascript type ApiResponse<T> = | { kind: "ok"; data: T } | { kind: "error"; message: string }; function handleResponse(res: ApiResponse<string>) { if (res.kind === "ok") { console.log("OK:", res.data); } else { console.error("Error:", res.message); } } ``` --- ## The difference from a regular union | Regular union | Discriminated union | |---|---| | `type A = {a: number} \| {b: string}` | `type A = { kind: "x"; a: number } \| { kind: "y"; b: string }` | | When checking properties, TypeScript does not know which variant it is | TypeScript automatically narrows the type by `kind` | | Checks must be done manually (`'a' in obj`) | Comparing the tag is enough (`obj.kind === 'x'`) | --- ## Summary | Concept | Description | |---|---| | **Discriminant (tag)** | A common property with unique literal values that determines the type variant | | **Discriminated union** | A union of types distinguished by a tag | | **Main advantage** | TypeScript narrows the type itself, based on the tag's value | | **Typical use** | Loading states, actions, result variants, finite state machines |For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.