Typescript discriminated unions
Created By: chatGPT
Discriminated unions in TypeScript allow us to define a type that can be one of several different types, usually distinguished by a common property that acts as a discriminator. This is especially useful when dealing with complex data structures where an entity can take on multiple shapes. Here's how you can implement it.
// Define the discriminated union type
interface Circle {
kind: 'circle';
radius: number;
}
interface Square {
kind: 'square';
sideLength: number;
}
type Shape = Circle | Square;
In this example, we have two interfaces:
Circle
and Square
, each with a kind
property that identifies which type it is. The union type Shape
can either be a Circle
or a Square
.function area(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength * shape.sideLength;
}
}
The
area
function takes a Shape
as an argument and uses a switch statement to determine the type of shape passed to it. This leverages the kind
property to differentiate between shapes and calculate their area accordingly. Here is how you might use this in practice.const myCircle: Circle = { kind: 'circle', radius: 10 };
const mySquare: Square = { kind: 'square', sideLength: 5 };
console.log(area(myCircle)); // Outputs: 314.159...
console.log(area(mySquare)); // Outputs: 25
This allows you to work more effectively with complex types and ensures type safety when dealing with different structures. If you try to call the
area
function with an incompatible shape, TypeScript will throw an error.const invalidShape = { kind: 'triangle', base: 5, height: 10 }; // TypeScript Error
console.log(area(invalidShape)); // Error: Argument of type '{ kind: string; base: number; height: number; }' is not assignable to parameter of type 'Shape'.
By using discriminated unions, you can also enforce exhaustiveness checks by making sure all possible cases are handled in switch statements or other conditional logic. This can help catch bugs at compile time.
function getDescription(shape: Shape): string {
switch (shape.kind) {
case 'circle':
return `Circle with radius ${shape.radius}`;
case 'square':
return `Square with side length ${shape.sideLength}`;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck; // This will ensure all cases are handled
}
}