What is a discriminated union and why is it useful?
A discriminated union is a union of object types that share a literal-typed property, such as kind or type. Checking that property narrows the whole object to one member, so each branch sees only the fields that belong to it.
Explain it simply
A UPI payment can come as card, bank transfer or wallet. Each has different details, but all carry a label saying which one it is. Check the label first, and you know exactly which other fields exist.
That label is the discriminant. It must be a literal type like "card", not just string. In a switch on it, TypeScript narrows the type in each case, and with a never check in default it can warn you when a new member is added and you forgot to handle it.
Example
type Payment =
| { kind: "card"; last4: string }
| { kind: "upi"; vpa: string }
| { kind: "cash" };
function describe(p: Payment): string {
switch (p.kind) {
case "card":
return `Card ending ${p.last4}`; // p: card variant
case "upi":
return `UPI id ${p.vpa}`; // p: upi variant
case "cash":
return "Cash";
default: {
const _never: never = p; // error if a case is missing
return _never;
}
}
}
console.log(describe({ kind: "upi", vpa: "asha@upi" }));What to say in the interview
"A discriminated union has a shared literal property, the tag, that tells TypeScript which member it is looking at. A switch on the tag narrows each case, and a never assignment in default makes the compiler flag unhandled variants. I use it for payment types, API results and state machines."
Common mistakes
- Making the tag a plain string, so no narrowing happens.
- Using optional fields on one big interface instead of separate union members.