Type predicates are used to determine if a given value belongs to a certain type or not, providing a hint to the TypeScript compiler about the type information.
What are Type Predicates ๐คโ
In TypeScript, discriminated unions (also known as tagged unions or algebraic data types) are a powerful feature that allows you to create and work with complex types in a type-safe and expressive manner. They are particularly useful for modeling state transitions and handling different cases in a structured way.
A discriminated union is a union type (a type that can be one of several types) where a common property, called the discriminant or tag, is used to determine which specific type within the union is being represented. Each type in the union has a unique value for the discriminant property, enabling TypeScript to distinguish between them and provide appropriate type checking and code completion.
Here's an example illustrating the concept of discriminated unions in TypeScript:
// Define a common "kind" property as the discriminant
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
// Create a discriminated union type
type Shape = Circle | Square | Rectangle;
// Function to calculate the area of a given shape
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius * shape.radius;
case "square":
return shape.sideLength * shape.sideLength;
case "rectangle":
return shape.width * shape.height;
default:
// Exhaustiveness checking
const _: never = shape;
return _;
}
}
In this example, Shape
is a discriminated union type consisting
of Circle
, Square
, and Rectangle
. The kind
property acts as the
discriminant. The getArea
function uses a switch statement to determine the
correct formula for calculating the area of a given shape, and TypeScript
ensures that all cases are properly handled, providing compile-time type
checking and error detection.
Time To Transition From JavaScript To TypeScript
Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.
SEE COURSE DETAILSHow is it Helpful ๐คโ
Discriminated unions in TypeScript are helpful and different from normal switch case statements or conditional statements in JavaScript in several ways and the most prominent of them all is exhaustiveness checking.
TypeScript can help you ensure that all possible cases of a discriminated union are handled in a switch statement. If a new case is added to the union type, TypeScript will raise a compilation error if the switch statement doesn't handle the new case. This can help you catch potential bugs early on during development.
Example of Exhaustiveness Checking With Discriminated Unions ๐คโ
In the example below, I have added a new interface called Triangle
and
included it in the Shape
discriminated union type. The getArea
function,
however, has not been updated to handle the new Triangle
case. As a result,
the TypeScript compiler will raise an error due to the exhaustiveness checking
provided by the discriminated unions.
// Define a common "kind" property as the discriminant
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
// Adding a new interface called Triangle
interface Triangle {
kind: "triangle";
base: number;
height: number;
}
// Updating the discriminated union type to include the new Triangle interface
type Shape = Circle | Square | Rectangle | Triangle;
// Function to calculate the area of a given shape
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius * shape.radius;
case "square":
return shape.sideLength * shape.sideLength;
case "rectangle":
return shape.width * shape.height;
// The new "triangle" case is missing, causing a compilation error
default:
// Exhaustiveness checking
const _: never = shape;
return _;
}
}
In this example, the TypeScript compiler will raise an error due to the
missing "triangle" case in the getArea
function's switch statement, enforcing
exhaustiveness checking. To fix the error, you would need to add a case to
handle the Triangle
type:
/** ...
case "triangle":
return (shape.base * shape.height) / 2;
**/
What Can You Do Next ๐๐โ
If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for ssoftware developers.