Tagged Unions and Exhaustive Type Checking - Textnotes

Tagged Unions and Exhaustive Type Checking


Learn how to use discriminated unions in TypeScript to handle multiple types safely. This module explains tagged unions, switch-based handling, and exhaustive type checking with practical examples.

1. Tagged Unions

Discriminated unions, also called tagged unions, use a common property to differentiate between multiple object types in a union.

Basic Example


interface Square {
kind: "square";
size: number;
}

interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}

type Shape = Square | Rectangle;

The kind property serves as a tag to identify the type of shape.

2. Switch-Based Handling

Tagged unions are often handled using switch statements, ensuring type-safe property access.

Example


function getArea(shape: Shape): number {
switch (shape.kind) {
case "square":
return shape.size * shape.size;
case "rectangle":
return shape.width * shape.height;
}
}

TypeScript narrows the type based on the kind property within each case.

Example with Functions


function describeShape(shape: Shape): string {
switch (shape.kind) {
case "square":
return `Square with size ${shape.size}`;
case "rectangle":
return `Rectangle with width ${shape.width} and height ${shape.height}`;
}
}

Switch-based handling ensures only valid properties are accessed for each type.

3. Exhaustive Type Checking

Exhaustive type checking ensures that all possible cases of a discriminated union are handled. This improves code safety and prevents runtime errors.

Example


function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}

function getPerimeter(shape: Shape): number {
switch (shape.kind) {
case "square":
return 4 * shape.size;
case "rectangle":
return 2 * (shape.width + shape.height);
default:
return assertNever(shape); // Ensures exhaustive checking
}
}

The assertNever function catches any unhandled cases at compile-time.

Conclusion

Discriminated unions in TypeScript provide a powerful pattern for handling multiple related types safely. Tagged properties, switch-based handling, and exhaustive type checking ensure type safety, reduce errors, and improve code maintainability in complex applications.