Careeroza
Discriminated Unions
A Common Tag Property
A discriminated union is a union of object types that all share one literal property (the discriminant, often type, kind or status). Checking it narrows to exactly one member. This is the most useful pattern in everyday TypeScript.
Example
type PaymentMethod =
| { type: "upi"; vpa: string }
| { type: "card"; last4: string; network: "VISA" | "RUPAY" | "MASTERCARD" }
| { type: "netbanking"; bank: string }
| { type: "cod" };
function describe(m: PaymentMethod): string {
switch (m.type) {
case "upi": return "UPI (" + m.vpa + ")";
case "card": return m.network + " card ending " + m.last4;
case "netbanking": return "Net banking via " + m.bank;
case "cod": return "Cash on delivery";
}
}
const methods: PaymentMethod[] = [
{ type: "upi", vpa: "ravi@okicici" },
{ type: "card", last4: "4242", network: "RUPAY" },
{ type: "netbanking", bank: "SBI" },
{ type: "cod" },
];
methods.forEach((m) => console.log(describe(m)));Output
UPI (ravi@okicici) RUPAY card ending 4242 Net banking via SBI Cash on delivery
Accessing the Wrong Member
Example
Inside the "upi" branch there is no last4 (TS2339).
type PaymentMethod =
| { type: "upi"; vpa: string }
| { type: "card"; last4: string };
function describe(m: PaymentMethod): string {
if (m.type === "upi") return m.last4;
return m.last4;
}Compiler output (tsc)
Line 6: error TS2339: Property 'last4' does not exist on type '{ type: "upi"; vpa: string; }'.Request State
Example
Modelling loading state as a discriminated union makes impossible states (loading AND error at the same time) impossible to write. React apps use this constantly.
type RequestState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; message: string };
function view(s: RequestState<string[]>): string {
if (s.status === "loading") return "Spinner...";
if (s.status === "error") return "Error: " + s.message;
if (s.status === "success") return s.data.length + " products";
return "Search for products";
}
console.log(view({ status: "idle" }));
console.log(view({ status: "loading" }));
console.log(view({ status: "success", data: ["Kettle", "Toaster"] }));
console.log(view({ status: "error", message: "Network down" }));Output
Search for products Spinner... 2 products Error: Network down