Ch. 2 · TypeScript

TypeScript Discriminated Unions for UI State

TypeScript Discriminated Unions for UI State. Learn the reasoning, a practical example, common mistakes and an interview exercise.

~2 min readbeginnerupdated Oct 3, 2026

A literal discriminant makes valid states explicit and supports narrowing. It prevents unrelated optional fields from forming impossible combinations.

Before you start

You should be comfortable with JavaScript values, functions and objects. Use a TypeScript project with strict checking enabled when trying the examples. Separate what the compiler proves from what must still be checked when values arrive at runtime.

The practical goal is to reason through this situation: A result is loading, success with data, or failure with an error. Read the walkthrough first, then try the interview exercise before opening its answer. The important part is explaining the decision and its consequences, rather than remembering a definition alone.

Step-by-step walkthrough

Step 1: List valid states

Write the states independently before defining fields. Only success needs data, while only failure needs an error message.

Step 2: Attach fields to the state

Use a literal kind property. Narrow on that property before accessing state-specific fields; optional fields are no longer necessary for unrelated states.

Step 3: Handle transitions deliberately

Loading replaces previous outcomes. Cancellation becomes its own union member if the interface needs to distinguish it from network failure.

Worked scenario

A result is loading, success with data, or failure with an error.

type Result =
  | { kind: 'loading' }
  | { kind: 'success'; data: string[] }
  | { kind: 'failure'; message: string };
function label(result: Result): string {
  switch (result.kind) {
    case 'loading': return 'Loading';
    case 'success': return `${result.data.length} results`;
    case 'failure': return result.message;
  }
}
TypeScript

The successful branch can read data without an assertion. A loading result cannot accidentally carry a required failure message because the model describes each state separately.

Common mistake

Separate booleans permit loading and success simultaneously.

Verify the behavior

Attempt to construct success without data and access data in the loading branch. Both should fail checking. Runtime input still needs validation before becoming Result.

Interview exercise

Add a cancelled state safely.

Answer and reasoning

Extend the union and update exhaustive consumers; cancellation need not pretend to be a server error.

Continue learning

Compare the scenario with the TypeScript interview questions and test your understanding with the TypeScript MCQs. For terminology and implementation details, consult the reference material.

More in TypeScript

read ✓TypeScript · hard

TypeScript Abstract Classes and Contracts

Share behavior with abstract classes, enforce required members, and decide when an interface or composition is the better contract.

~2 min readread →
read ✓TypeScript · mid

TypeScript Enums vs Union Types

Compare enums with unions of string literals: runtime cost, serialization, exhaustiveness and which one fits application code.

~3 min readread →
esc