What if your code could instantly know exactly what kind of data it's dealing with, avoiding costly mistakes?
Why Discriminated unions in Typescript? - Purpose & Use Cases
Imagine you have different types of shapes like circles and squares, and you want to write code that handles each shape differently. Without a clear way to tell which shape you have, you might write many if-else checks scattered everywhere.
This manual approach is slow and confusing. You might forget a case, mix up properties, or write repetitive code. It's easy to make mistakes and hard to maintain as your program grows.
Discriminated unions let you tag each shape with a unique label. This way, TypeScript knows exactly which shape you have and what properties it has. Your code becomes cleaner, safer, and easier to understand.
if (shape.type === 'circle') { // handle circle } else if (shape.type === 'square') { // handle square }
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; size: number };
function area(shape: Shape) {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.size ** 2;
}
}It enables writing clear, error-free code that safely handles different data types with confidence.
Think of a messaging app where messages can be text, images, or videos. Discriminated unions help you handle each message type correctly without confusion.
Manual type checks are error-prone and messy.
Discriminated unions tag data with unique labels for clarity.
This leads to safer, cleaner, and easier-to-maintain code.
Practice
What is the main purpose of a discriminated union in TypeScript?
Solution
Step 1: Understand discriminated unions
Discriminated unions use a shared property (called the discriminant) to identify which type an object belongs to.Step 2: Purpose of discriminated unions
This helps TypeScript safely narrow types in code by checking that property.Final Answer:
To distinguish between different object types using a common property -> Option DQuick Check:
Discriminated union = common property to tell types apart [OK]
- Thinking discriminated unions convert types automatically
- Confusing with simple unions of primitives
- Believing they enforce one single type only
Which of the following is the correct syntax to define a discriminated union type in TypeScript?
type Shape = ?Solution
Step 1: Identify discriminant property
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number } uses a common propertykindwith literal types ('circle' or 'square') to discriminate.Step 2: Check union structure
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number } correctly uses a union of two object types distinguished bykind.Final Answer:
type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number } -> Option AQuick Check:
Discriminated union = union with common literal property [OK]
- Using intersection (&) instead of union (|)
- Missing literal type for discriminant property
- Using optional properties without discriminant
What will be the output of the following TypeScript code?
type Animal =
| { type: 'dog'; barkVolume: number }
| { type: 'cat'; livesLeft: number };
function describe(animal: Animal) {
switch (animal.type) {
case 'dog':
return `Dog barks at volume ${animal.barkVolume}`;
case 'cat':
return `Cat has ${animal.livesLeft} lives left`;
}
}
console.log(describe({ type: 'cat', livesLeft: 7 }));Solution
Step 1: Analyze the input object
The input is{ type: 'cat', livesLeft: 7 }, so thetypeis 'cat'.Step 2: Follow the switch case
The switch matches 'cat' case and returns`Cat has ${animal.livesLeft} lives left`, which becomes 'Cat has 7 lives left'.Final Answer:
Cat has 7 lives left -> Option AQuick Check:
Switch on discriminant returns correct string [OK]
- Confusing 'dog' and 'cat' cases
- Expecting runtime error due to union
- Ignoring the discriminant property
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;
}
}Solution
Step 1: Check property access in else branch
In the else branch, the code accessesvehicle.wheels, butbiketype has nowheelsproperty.Step 2: Understand TypeScript error
This causes a type error becausewheelsis not defined for{ kind: 'bike'; pedals: number }.Final Answer:
Property 'wheels' does not exist on type 'bike' object -> Option BQuick Check:
Accessing wrong property on union member causes error [OK]
- Assuming all union members share same properties
- Ignoring TypeScript error messages
- Thinking switch is mandatory over if
Given the following discriminated union, how can you safely add a new shape type rectangle with width and height properties and update the area function accordingly?
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;
}
}Solution
Step 1: Extend the union type
Add the new shape type with discriminantkind: 'rectangle'and propertieswidthandheightto theShapeunion.Step 2: Update the area function
Add a new case in the switch for 'rectangle' that returnsshape.width * shape.heightto correctly calculate the area.Final Answer:
Add { kind: 'rectangle'; width: number; height: number } to Shape union and add case 'rectangle' in switch returning width * height -> Option CQuick Check:
Extend union and handle new case in switch [OK]
- Forgetting to update the function for new type
- Removing existing cases accidentally
- Not using discriminant property in new type
Ready to prove it?
Pass the test and this pattern moves unknown
