Good Code
The good version makes the final branch impossible when all union cases are handled, so adding a new state creates a compile-time reminder.
Lesson 05
Make every union case handled so new states fail loudly during development.
type ReviewState =
| { status: "draft" }
| { status: "open"; reviewerCount: number }
| { status: "merged"; mergedBy: string };
function renderReviewState(state: ReviewState) {
// Assigning to never makes missing union cases a compile error.
switch (state.status) {
case "draft":
return "Draft";
case "open":
return "Open for " + state.reviewerCount + " reviewers";
case "merged":
return "Merged by " + state.mergedBy;
default: {
const neverState: never = state;
return neverState;
}
}
}type ReviewState =
| { status: "draft" }
| { status: "open"; reviewerCount: number }
| { status: "merged"; mergedBy: string };
function renderReviewState(state: ReviewState) {
// Generic fallback hides states the code forgot to handle.
if (state.status === "draft") {
return "Draft";
}
if (state.status === "open") {
return "Open for " + state.reviewerCount + " reviewers";
}
return "Unknown state";
}The good version makes the final branch impossible when all union cases are handled, so adding a new state creates a compile-time reminder.
The bad version hides missing cases behind a generic fallback, which can make new product states render as unknown instead of asking the developer to handle them.