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
A discriminated union is a union of object types that all share one literal field, often called type
, kind
, 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:
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 callshandleResult({ type: "success", data: "Form submitted!" });handleResult({ type: "error", error: "Missing required fields." });
Explanation:
Lines 1–9: Define two object ...