...

/

Discriminated Unions and Exhaustive Checks

Discriminated Unions and Exhaustive Checks

Model variant object types with a discriminant field and use exhaustive checks to ensure no case is left unhandled.

We’ve narrowed union types using built-in checks like typeof, and even written our own custom type guards to distinguish structured variants at runtime. That gave us fine-grained control. But sometimes, the shape of the data itself tells us everything we need to know without writing a single guard.

Think server responses. Think user actions. Think result states. These aren’t just values floating around—they’re structured variants. Each one carries a tag that tells us exactly what it is. And once we know that, the rest of its shape falls into place.

If we want to model those cleanly and make sure we never miss a case—we need discriminated unions.

Structuring union types with tags

discriminated union is a union of object types that all share one literal field, often called typekind, or status. This field is the discriminant: a tag that tells TypeScript exactly which shape we’re working with.

To see this in action, imagine a form submission. The result could be a success or an error:

Press + to interact
TypeScript 5.8.3
Files
type Success = {
type: "success";
data: string;
};
type FormError = {
type: "error";
error: string;
};
type FormResult = Success | FormError;
function handleResult(result: FormResult) {
if (result.type === "success") {
console.log("Submitted:", result.data);
} else {
console.log("Error:", result.error);
}
}
// Sample calls
handleResult({ type: "success", data: "Form submitted!" });
handleResult({ type: "error", error: "Missing required fields." });

Explanation:

  • Lines 1–9: Define two object ...