Bird
Raised Fist0
Typescriptprogramming~20 mins

Discriminated union state machines in Typescript - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Discriminated Union Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ Predict Output
intermediate
2:00remaining
Output of a simple discriminated union state machine
What is the output of this TypeScript code that uses a discriminated union to represent states?
Typescript
type State =
  | { type: "loading" }
  | { type: "success"; data: string }
  | { type: "error"; message: string };

function getMessage(state: State): string {
  switch (state.type) {
    case "loading":
      return "Loading...";
    case "success":
      return `Data: ${state.data}`;
    case "error":
      return `Error: ${state.message}`;
  }
}

const currentState: State = { type: "success", data: "Hello" };
console.log(getMessage(currentState));
ALoading...
BError: Hello
CData: Hello
Dundefined
Attempts:
2 left
💡 Hint
Look at the type property of the currentState object and how the switch statement handles it.
❓ Predict Output
intermediate
2:00remaining
Result of state transition function
Given this state machine transition function, what is the value of nextState after calling transition(currentState, "fail")?
Typescript
type State =
  | { type: "idle" }
  | { type: "loading" }
  | { type: "success"; data: number }
  | { type: "error"; message: string };

function transition(state: State, event: "start" | "succeed" | "fail"): State {
  switch (state.type) {
    case "idle":
      if (event === "start") return { type: "loading" };
      break;
    case "loading":
      if (event === "succeed") return { type: "success", data: 42 };
      if (event === "fail") return { type: "error", message: "Failed to load" };
      break;
    case "success":
    case "error":
      if (event === "start") return { type: "loading" };
      break;
  }
  return state;
}

const currentState: State = { type: "loading" };
const nextState = transition(currentState, "fail");
A{"type":"error","message":"Failed to load"}
B{"type":"success","data":42}
C{"type":"loading"}
D{"type":"idle"}
Attempts:
2 left
💡 Hint
Check the transition function's case for "loading" state and "fail" event.
🔧 Debug
advanced
2:00remaining
Identify the error in this discriminated union state machine code
This TypeScript code tries to handle states with a discriminated union, but it causes a compile-time error. What is the error?
Typescript
type State =
  | { status: "open" }
  | { status: "closed"; reason: string };

function handleState(state: State) {
  if (state.status === "open") {
    console.log("Open state");
  } else {
    console.log(state.reason);
  }
}

const s: State = { status: "open" };
handleState(s);
ACannot compare 'state.status' with string literal.
BType 'State' is not assignable to parameter of type 'string'.
CMissing return statement in function 'handleState'.
DProperty 'reason' does not exist on type '{ status: "open" }'.
Attempts:
2 left
💡 Hint
Check the else branch and what properties are accessed on the state object.
📝 Syntax
advanced
2:00remaining
Which option correctly defines a discriminated union for a state machine?
Choose the correct TypeScript code snippet that defines a discriminated union type for a state machine with states: 'start', 'processing' with a progress number, and 'done' with a result string.
Atype State = { type: "start" } & { type: "processing", progress: number } & { type: "done", result: string };
Btype State = { type: "start" } | { type: "processing", progress: number } | { type: "done", result: string };
Ctype State = { type: "start" | "processing" | "done"; progress?: number; result?: string };
Dtype State = { type: string; progress: number; result: string };
Attempts:
2 left
💡 Hint
Discriminated unions use the '|' operator to combine distinct object types with a common discriminant property.
🚀 Application
expert
2:00remaining
Determine the number of possible states in this discriminated union state machine
Consider this TypeScript discriminated union representing a state machine. How many distinct states does it represent?
Typescript
type State =
  | { phase: "init" }
  | { phase: "running"; step: 1 | 2 | 3 }
  | { phase: "finished"; success: boolean };

// Count all possible distinct states represented by this type.
A6
B5
C7
D8
Attempts:
2 left
💡 Hint
Count each variant and multiply by the number of possible values for properties with 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