Bird
Raised Fist0
Typescriptprogramming~10 mins

Discriminated union state machines in Typescript - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Discriminated union state machines
Start: Initial State
↓
Check current state's 'type'
↓
Match 'type' with case
↓
StateA
↓
Perform action
↓
Transition to next state
↓
Repeat or End
The machine starts in an initial state, checks the state's 'type' property, matches it to a case, performs actions, then transitions to the next state, repeating until done.
Execution Sample
Typescript
type State =
  | { type: 'idle' }
  | { type: 'loading' }
  | { type: 'success'; data: string };

function nextState(state: State): State {
  switch(state.type) {
    case 'idle': return { type: 'loading' };
    case 'loading': return { type: 'success', data: 'Done!' };
    case 'success': return { type: 'idle' };
  }
}
This code defines a state machine with three states and a function to move to the next state based on the current state's type.
Execution Table
StepCurrent StateState.typeActionNext State
1{ type: 'idle' }idleMatch 'idle' case, transition to 'loading'{ type: 'loading' }
2{ type: 'loading' }loadingMatch 'loading' case, transition to 'success' with data{ type: 'success', data: 'Done!' }
3{ type: 'success', data: 'Done!' }successMatch 'success' case, transition to 'idle'{ type: 'idle' }
4{ type: 'idle' }idleCycle repeats or stopsDepends on usage
💡 The machine cycles through states by matching the 'type' property and transitioning accordingly.
Variable Tracker
VariableStartAfter Step 1After Step 2After Step 3After Step 4
state{ type: 'idle' }{ type: 'loading' }{ type: 'success', data: 'Done!' }{ type: 'idle' }Depends on usage
Key Moments - 3 Insights
Why do we use the 'type' property to decide the next state?
The 'type' property uniquely identifies each state variant, allowing the switch-case to safely and clearly select the correct transition, as shown in execution_table steps 1-3.
What happens if we add a new state but forget to handle it in the switch?
TypeScript will warn about missing cases if using exhaustive checks, preventing runtime errors. Without handling, the machine might return undefined or behave unexpectedly, breaking the flow seen in execution_table.
Why does the 'success' state include extra data?
Some states carry additional information needed for actions or transitions. Here, 'success' holds 'data' to represent result info, which is part of the discriminated union pattern.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table at Step 2. What is the 'state.type' value?
Aloading
Bsuccess
Cidle
Derror
💡 Hint
Check the 'State.type' column in execution_table row for Step 2.
At which step does the state include extra data besides 'type'?
AStep 1
BStep 2
CStep 3
DStep 4
💡 Hint
Look at the 'Current State' column and find where 'data' appears.
If we add a new state 'error' but do not add a case for it, what likely happens?
AThe machine crashes immediately.
BThe machine transitions to 'idle' automatically.
CTypeScript will warn about missing case handling.
DThe 'error' state is handled by default.
💡 Hint
Refer to key_moments about missing switch cases and TypeScript checks.
Concept Snapshot
Discriminated union state machines use a 'type' property to identify states.
Use a switch on 'type' to decide transitions.
Each state can have extra data.
TypeScript ensures all states are handled.
This pattern makes state logic clear and safe.
Full Transcript
This example shows a state machine in TypeScript using discriminated unions. The machine has three states: 'idle', 'loading', and 'success'. Each state is an object with a 'type' property. The function nextState takes the current state and uses a switch on state.type to decide the next state. It moves from 'idle' to 'loading', then to 'success' with data, then back to 'idle'. The execution table traces each step, showing the current state, the matched type, the action taken, and the next state. The variable tracker shows how the 'state' variable changes after each step. Key moments explain why the 'type' property is important, what happens if a state is not handled, and why some states carry extra data. The visual quiz tests understanding of state types at steps, presence of extra data, and TypeScript's role in catching missing cases. The snapshot summarizes the pattern as a safe, clear way to manage state transitions using discriminated unions.

Practice

(1/5)
1.

What is the main purpose of using a discriminated union in TypeScript state machines?

easy
A. To enable dynamic addition of states at runtime without type errors.
B. To clearly define different states with a common type property for safe type checking.
C. To allow any property names in the state objects without restrictions.
D. To automatically generate UI components based on state names.

Solution

  1. Step 1: Understand discriminated unions

    Discriminated unions use a common type property to distinguish between different object shapes.
  2. Step 2: Identify the purpose in state machines

    This common property allows TypeScript to safely narrow the state type and catch errors.
  3. Final Answer:

    To clearly define different states with a common type property for safe type checking. -> Option B
  4. Quick Check:

    Discriminated union = safe state typing [OK]
Hint: Look for the common type property in states [OK]
Common Mistakes:
  • Thinking discriminated unions allow any property names freely
  • Assuming states can be added dynamically without type errors
  • Confusing discriminated unions with UI generation
2.

Which of the following is the correct syntax to define a discriminated union type for a state machine with states loading and success?

type State = ?
easy
A. { status: 'loading' } | { status: 'success'; data: string }
B. { type: 'loading'; data: string } | { type: 'success' }
C. { type: 'loading' } | { type: 'success'; data: string }
D. { state: 'loading' } | { state: 'success'; data: string }

Solution

  1. Step 1: Identify the discriminant property

    The discriminated union requires a common property named type to distinguish states.
  2. Step 2: Match the correct state shapes

    Loading state has only type: 'loading', success state has type: 'success' and extra data.
  3. Final Answer:

    { type: 'loading' } | { type: 'success'; data: string } -> Option C
  4. Quick Check:

    Common type property with correct fields = B [OK]
Hint: Discriminant property must be named exactly 'type' [OK]
Common Mistakes:
  • Using different property names like 'status' or 'state'
  • Mixing properties between states incorrectly
  • Adding data to loading state incorrectly
3.

Given the following TypeScript code, what will be the output?

type State =
  | { type: 'idle' }
  | { type: 'loading' }
  | { type: 'success'; data: string };

function getMessage(state: State): string {
  switch (state.type) {
    case 'idle':
      return 'Waiting...';
    case 'loading':
      return 'Loading...';
    case 'success':
      return `Data: ${state.data}`;
  }
}

console.log(getMessage({ type: 'success', data: 'Hello' }));
medium
A. Error: Property 'data' does not exist
B. Loading...
C. Waiting...
D. Data: Hello

Solution

  1. Step 1: Analyze the input state

    The input state is { type: 'success', data: 'Hello' }, matching the 'success' case.
  2. Step 2: Follow the switch case logic

    For 'success', the function returns `Data: ${state.data}`, which becomes 'Data: Hello'.
  3. Final Answer:

    Data: Hello -> Option D
  4. Quick Check:

    State 'success' returns data string [OK]
Hint: Match type in switch to get correct output [OK]
Common Mistakes:
  • Confusing 'success' with 'loading' or 'idle'
  • Expecting an error accessing 'data' without checking type
  • Ignoring template string interpolation
4.

Identify the error in this discriminated union state machine code:

type State =
  | { type: 'start' }
  | { type: 'end'; result: number };

function process(state: State) {
  if (state.type === 'start') {
    console.log('Starting');
  } else {
    console.log('Result is ' + state.result);
  }
}

process({ type: 'start' });
medium
A. No error; code runs and prints 'Starting'.
B. Error: Accessing 'result' on 'start' state causes runtime error.
C. Error: Missing return type annotation on function.
D. Error: 'type' property should be optional.

Solution

  1. Step 1: Check the state type conditions

    The function checks if state.type === 'start' and logs 'Starting'.
  2. Step 2: Confirm safe access to 'result'

    In the else branch, state.result is accessed only if state.type !== 'start', so it must be 'end' with result.
  3. Final Answer:

    No error; code runs and prints 'Starting'. -> Option A
  4. Quick Check:

    Type narrowing prevents invalid property access [OK]
Hint: Check type guards before accessing state-specific properties [OK]
Common Mistakes:
  • Assuming accessing 'result' on 'start' state causes error
  • Expecting return type annotation is mandatory
  • Thinking 'type' property should be optional
5.

You want to extend a discriminated union state machine to handle an error state with an optional message. Which is the best way to define this state to keep type safety and allow missing message?

type State =
  | { type: 'idle' }
  | { type: 'loading' }
  | { type: 'success'; data: string }
  | ???
hard
A. { type: 'error'; message?: string }
B. { type: 'error'; message: string | undefined }
C. { type: 'error'; message: string }
D. { type: 'error'; message: string | null }

Solution

  1. Step 1: Understand optional properties in discriminated unions

    Using message?: string means the property can be missing or a string, which fits optional message.
  2. Step 2: Compare other options

    { type: 'error'; message: string | undefined } requires the property but allows undefined explicitly, less idiomatic. { type: 'error'; message: string } requires message always. { type: 'error'; message: string | null } requires message but allows null, which is less type safe.
  3. Final Answer:

    { type: 'error'; message?: string } -> Option A
  4. Quick Check:

    Optional property syntax keeps type safety and flexibility [OK]
Hint: Use '?' to mark optional properties in state types [OK]
Common Mistakes:
  • Making message required when it should be optional
  • Using union with undefined instead of optional property
  • Allowing null which can cause runtime issues
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard