Bird
Raised Fist0
Typescriptprogramming~15 mins

Discriminated union narrowing in Typescript - Mini Project: Build & Apply

Choose your learning style10 modes available
Discriminated Union Narrowing in TypeScript
📖 Scenario: You are building a simple program that handles different shapes. Each shape has a type and specific properties. You want to write code that safely works with each shape type.
🎯 Goal: Create a discriminated union of shapes and write code that narrows the type based on the kind property to calculate the area.
📋 What You'll Learn
Create a union type called Shape with two variants: Circle and Rectangle
Each variant must have a kind property with a unique string literal
Add properties specific to each shape: radius for circle, width and height for rectangle
Write a function getArea that takes a Shape and returns its area using discriminated union narrowing
Use a switch statement on the kind property inside getArea
💡 Why This Matters
🌍 Real World
Discriminated unions are useful when working with data that can be one of several types, like different shapes, events, or messages.
💼 Career
Understanding discriminated union narrowing is important for writing safe and clear TypeScript code in frontend and backend development.
Progress0 / 4 steps
1
Create the Shape union type
Create a type called Shape that is a union of two object types: one with kind set to "circle" and a radius number property, and another with kind set to "rectangle" and width and height number properties.
Typescript
Hint

Use a union type with | and include the kind property with string literals.

2
Create a sample shape variable
Create a constant called myShape and assign it an object with kind set to "circle" and radius set to 5.
Typescript
Hint

Assign an object matching the circle variant of Shape.

3
Write the getArea function with discriminated union narrowing
Write a function called getArea that takes a parameter shape of type Shape. Use a switch statement on shape.kind to return the area: for "circle", return Math.PI * shape.radius ** 2; for "rectangle", return shape.width * shape.height.
Typescript
Hint

Use a switch on shape.kind to narrow the type and calculate the area accordingly.

4
Print the area of myShape
Write a console.log statement that prints the result of calling getArea with myShape.
Typescript
Hint

Call getArea(myShape) and print the result with console.log.

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