Welcome back! I am Mihir, and in this lesson we will learn discriminated unions in TypeScript.
A discriminated union is a union where each member has a shared property with a unique literal value.
Basic Discriminated Union
type LoadingState = {
status: "loading";
};
type SuccessState = {
status: "success";
data: string[];
};
type ErrorState = {
status: "error";
message: string;
};
type RequestState = LoadingState | SuccessState | ErrorState;The status property is the discriminator.
Narrowing with the Discriminator
function render(state: RequestState) {
if (state.status === "success") {
console.log(state.data);
}
if (state.status === "error") {
console.log(state.message);
}
}When status is "success", TypeScript knows state has data.
When status is "error", TypeScript knows state has message.
Using switch
function getMessage(state: RequestState) {
switch (state.status) {
case "loading":
return "Loading...";
case "success":
return `Loaded ${state.data.length} items`;
case "error":
return state.message;
}
}switch works beautifully with discriminated unions.
Real Example: Payment
type PendingPayment = {
kind: "pending";
amount: number;
};
type CompletedPayment = {
kind: "completed";
amount: number;
transactionId: string;
};
type FailedPayment = {
kind: "failed";
reason: string;
};
type Payment = PendingPayment | CompletedPayment | FailedPayment;Usage:
function describePayment(payment: Payment) {
switch (payment.kind) {
case "pending":
return `Pending: ${payment.amount}`;
case "completed":
return `Transaction: ${payment.transactionId}`;
case "failed":
return `Failed: ${payment.reason}`;
}
}Why This Pattern Is Powerful
Without a discriminator, you often need property checks.
With a discriminator, TypeScript has one clear field to inspect.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; size: number };This is easier to narrow than guessing based on random properties.
Choose a Clear Discriminator Name
Common names include:
typekindstatusstate
Pick one that matches your domain.
Quick Recap
- A discriminated union uses a shared literal property.
- The shared property is called the discriminator.
- Checking the discriminator narrows the union.
switchstatements are very readable with this pattern.- This is one of the best ways to model states in TypeScript.
Next up, we will learn Exhaustive Checks →.