·#TypeScript#Patterns
Discriminated Unions Are the Best Part of TypeScript
Replacing optional flags with a discriminant property cleans up component props and unlocks exhaustive checks.
Discriminated unions let TypeScript narrow types based on a literal tag. They turn fuzzy "any of these flags might be set" props into precise, mutually exclusive shapes.
The problem
A typical Button component starts simple, then grows props for every variant: href, onClick, isLoading, disabled. Soon callers can pass nonsense like href and onClick at the same time.
The fix
type ButtonProps =
| { kind: "link"; href: string; children: React.ReactNode }
| { kind: "button"; onClick: () => void; children: React.ReactNode };
function Button(props: ButtonProps) {
if (props.kind === "link") {
return <a href={props.href}>{props.children}</a>;
}
return <button onClick={props.onClick}>{props.children}</button>;
}
Now the wrong props are unreachable. Pair with an assertNever helper in switches for exhaustiveness.
Rule of thumb: if two props should never appear together, they belong to different variants of a union.