Bird
Raised Fist0
Typescriptprogramming~20 mins

Discriminated union narrowing 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 discriminated union narrowing with switch
What is the output of the following TypeScript code when calling handleShape({ kind: 'circle', radius: 5 })?
Typescript
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; size: number };

function handleShape(shape: Shape): string {
  switch (shape.kind) {
    case 'circle':
      return `Circle with radius ${shape.radius}`;
    case 'square':
      return `Square with size ${shape.size}`;
  }
}

console.log(handleShape({ kind: 'circle', radius: 5 }));
A"Circle with radius 5"
BCompilation error
CTypeError at runtime
D"Square with size 5"
Attempts:
2 left
💡 Hint
Look at the kind property and how the switch narrows the type.
❓ Predict Output
intermediate
2:00remaining
Output of discriminated union narrowing with if statement
What will the following code output when calling describeAnimal({ type: 'dog', barkVolume: 10 })?
Typescript
type Animal =
  | { type: 'dog'; barkVolume: number }
  | { type: 'cat'; livesLeft: number };

function describeAnimal(animal: Animal): string {
  if (animal.type === 'dog') {
    return `Dog barks at volume ${animal.barkVolume}`;
  } else {
    return `Cat has ${animal.livesLeft} lives left`;
  }
}

console.log(describeAnimal({ type: 'dog', barkVolume: 10 }));
A"Cat has 10 lives left"
B"Dog barks at volume 10"
CRuntime error: property undefined
DCompilation error
Attempts:
2 left
💡 Hint
Check how the if condition narrows the union type.
❓ Predict Output
advanced
2:00remaining
Output of function using discriminated union with exhaustive check
What is the output of the following code when calling getArea({ kind: 'rectangle', width: 4, height: 3 })?
Typescript
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'rectangle'; width: number; height: number };

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'rectangle':
      return shape.width * shape.height;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

console.log(getArea({ kind: 'rectangle', width: 4, height: 3 }));
ARuntime error: _exhaustiveCheck is never
BNaN
C12
DCompilation error due to never type
Attempts:
2 left
💡 Hint
Look at how the switch handles the 'rectangle' case and the default.
❓ Predict Output
advanced
2:00remaining
Output of discriminated union with nested narrowing
What will the following code output when calling processEvent({ type: 'click', payload: { x: 10, y: 20 } })?
Typescript
type Event =
  | { type: 'click'; payload: { x: number; y: number } }
  | { type: 'keypress'; payload: { key: string } };

function processEvent(event: Event): string {
  if (event.type === 'click') {
    const { x, y } = event.payload;
    return `Clicked at (${x}, ${y})`;
  } else if (event.type === 'keypress') {
    return `Key pressed: ${event.payload.key}`;
  }
  return 'Unknown event';
}

console.log(processEvent({ type: 'click', payload: { x: 10, y: 20 } }));
ACompilation error
B"Key pressed: 10"
CRuntime error: property undefined
D"Clicked at (10, 20)"
Attempts:
2 left
💡 Hint
Check how the nested payload is accessed after narrowing by type.
🧠 Conceptual
expert
2:00remaining
Why does the never type help in discriminated union narrowing?
In TypeScript, why is the never type used in the default case of a switch statement handling a discriminated union?
ATo ensure all possible union members are handled, causing a compile error if any are missed
BTo allow the function to return any type without error
CTo optimize the code for faster execution
DTo catch runtime errors when an unknown value is passed
Attempts:
2 left
💡 Hint
Think about how TypeScript checks for completeness in union handling.

Practice

(1/5)
1.

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

easy
A. To force all types in a union to have the same properties
B. To create a union of unrelated types without any common property
C. To prevent any type narrowing in TypeScript
D. To use a common property to distinguish between different types in a union

Solution

  1. Step 1: Understand discriminated union concept

    A discriminated union uses a shared property (called discriminant) to identify which type is currently in use.
  2. Step 2: Identify the purpose of the discriminant property

    This shared property helps TypeScript narrow the type safely and access type-specific properties.
  3. Final Answer:

    To use a common property to distinguish between different types in a union -> Option D
  4. Quick Check:

    Discriminated union = common property for type narrowing [OK]
Hint: Look for a shared property that identifies the type [OK]
Common Mistakes:
  • Thinking discriminated unions require identical properties
  • Believing unions can't be narrowed by properties
  • Confusing discriminated unions with type intersections
2.

Which of the following is the correct syntax to define a discriminated union type in TypeScript?

type Shape = ?
easy
A. type Shape = { radius: number } & { side: number }
B. type Shape = { circle: number } | { square: number }
C. type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number }
D. type Shape = { kind: number; radius: number } | { kind: number; side: number }

Solution

  1. Step 1: Check for a shared discriminant property

    type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number } uses 'kind' as a string literal property to distinguish types, which is correct.
  2. Step 2: Verify property types and union structure

    type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number } correctly defines two object types with 'kind' discriminant and specific properties.
  3. Final Answer:

    type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number } -> Option C
  4. Quick Check:

    Discriminated union needs a shared literal property [OK]
Hint: Use a string literal property as discriminant [OK]
Common Mistakes:
  • Using & instead of | for union
  • Not using a literal type for discriminant
  • Using number instead of string literal for discriminant
3.

What will be the output of the following TypeScript code?

type Animal =
  | { type: 'dog'; barkVolume: number }
  | { type: 'cat'; livesLeft: number };

function describe(animal: Animal) {
  if (animal.type === 'dog') {
    return `Dog barks at volume ${animal.barkVolume}`;
  } else {
    return `Cat has ${animal.livesLeft} lives left`;
  }
}

console.log(describe({ type: 'cat', livesLeft: 7 }));
medium
A. "Cat has 7 lives left"
B. "Dog barks at volume 7"
C. Type error at compile time
D. Runtime error accessing property

Solution

  1. Step 1: Analyze the input object and type check

    The input has type 'cat' and livesLeft 7, matching the second union member.
  2. Step 2: Follow the if condition and return value

    Since animal.type is 'cat', the else branch runs, returning "Cat has 7 lives left".
  3. Final Answer:

    "Cat has 7 lives left" -> Option A
  4. Quick Check:

    Discriminant 'type' guides correct property access [OK]
Hint: Check the discriminant property value to pick branch [OK]
Common Mistakes:
  • Assuming wrong branch runs
  • Expecting compile or runtime errors
  • Confusing property names between types
4.

Identify the error in this TypeScript code using discriminated unions:

type Vehicle =
  | { kind: 'car'; wheels: number }
  | { kind: 'bike'; pedals: number };

function getWheels(vehicle: Vehicle) {
  if (vehicle.kind === 'car') {
    return vehicle.wheels;
  } else {
    return vehicle.wheels;
  }
}
medium
A. Missing return type annotation on function
B. Accessing 'wheels' on bike type causes error
C. Incorrect discriminant property name
D. No error, code is correct

Solution

  1. Step 1: Check property access in each branch

    In the else branch, vehicle.kind is 'bike', but code accesses vehicle.wheels which does not exist on bike.
  2. Step 2: Understand TypeScript error

    This causes a compile-time error because 'wheels' is not a property of the bike type.
  3. Final Answer:

    Accessing 'wheels' on bike type causes error -> Option B
  4. Quick Check:

    Access only properties valid for narrowed type [OK]
Hint: Check property exists on all union members before access [OK]
Common Mistakes:
  • Assuming all properties exist on all types
  • Ignoring discriminant for narrowing
  • Thinking missing return type causes error
5.

Given the following discriminated union, how can you safely add a new shape type { kind: 'triangle'; base: number; height: number } and update the area function to handle it?

type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; side: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.side ** 2;
  }
}
hard
A. Add triangle to Shape union and add a case 'triangle' in switch returning 0.5 * base * height
B. Add triangle to Shape union but do not change area function
C. Add triangle to Shape union and add a default case returning -1
D. Add triangle to Shape union and remove switch statement

Solution

  1. Step 1: Extend the discriminated union type

    Add { kind: 'triangle'; base: number; height: number } to the Shape union to include the new shape.
  2. Step 2: Update the area function switch

    Add a new case 'triangle' that calculates area as 0.5 * base * height to handle the new shape safely.
  3. Final Answer:

    Add triangle to Shape union and add a case 'triangle' in switch returning 0.5 * base * height -> Option A
  4. Quick Check:

    Update union and switch for new discriminant case [OK]
Hint: Add new type and matching switch case for safe narrowing [OK]
Common Mistakes:
  • Forgetting to update the function for new type
  • Using default case instead of explicit case
  • Removing switch and losing type safety
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