Discriminated unions are a powerful feature in TypeScript that allow you to work with multiple types in a type-safe manner. They combine Union Types with a common, literal property that distinguishes between the union members.
A discriminated union, also known as a tagged union or sum type, consists of several types that share a common, discriminant property. This property acts as a "tag" to identify which specific type is being used.
To create a discriminated union, define interfaces or types with a shared, literal property:
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; sideLength: number }
| { kind: "rectangle"; width: number; height: number };
In this example, kind
is the discriminant property. It helps TypeScript narrow down the specific type within the union.
You can use discriminated unions with switch statements or if-else chains for type-safe operations:
function calculateArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
case "rectangle":
return shape.width * shape.height;
}
}
Discriminated unions offer several advantages:
Common use cases include:
kind
, type
, or variant
).Extract
to work with specific members of the union.Here's a more complex example demonstrating how discriminated unions can be used for state management in a simple application:
type AppState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: string }
| { status: "error"; error: Error };
function handleState(state: AppState) {
switch (state.status) {
case "idle":
console.log("Application is idle");
break;
case "loading":
console.log("Loading data...");
break;
case "success":
console.log(`Data loaded: ${state.data}`);
break;
case "error":
console.error(`Error occurred: ${state.error.message}`);
break;
}
}
This pattern ensures type safety while handling different application states, making it easier to manage complex state transitions and associated data.
Discriminated unions are a cornerstone of TypeScript's type system, enabling developers to write more robust and maintainable code. By combining the flexibility of union types with the safety of literal types, they provide a powerful tool for modeling complex data structures and application states.
To further enhance your TypeScript skills, explore related concepts such as Intersection Types and Conditional Types.