Lesson 13 of 80
A union of object shapes that all share one common literal-typed field — checking that one field is enough for the compiler to know everything else about the shape, no type guard needed.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
Why must the discriminant field (like `status`) be a literal type rather than plain `string` for this narrowing to work?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
type State =
| { status: "loading" }
| { status: "success"; data: string }
| { status: "error"; message: string };
function render(state: State) {
if (state.status === "success") {
console.log(state.data); // ✅ narrowed — TS knows this branch only has `data`
}
if (state.status === "error") {
console.log(state.message); // ✅ narrowed to the error shape
}
}
The shared status field — called the discriminant — is a literal type in every branch of the union. Checking state.status === "success" doesn't just compare a string; it narrows state's entire type down to exactly the union member with status: "success", so TypeScript now knows data exists (and message doesn't).
State could be any of three shapes: loading, success{data}, or error{message}.
status were typed as plain string instead of the literal "loading" | "success" | "error", checking state.status === "success" wouldn't narrow anything — TypeScript needs the literal to match a specific union member.never and exhaustiveness checking: a switch over every discriminant value, with a default branch that assigns state to a variable typed never, makes the compiler itself flag it as an error if a new state shape is ever added and a case is forgotten.{ status: string; data?: string; message?: string } lets you accidentally have both data and message set, or neither — a discriminated union makes the invalid combinations impossible to construct in the first place.Modeling API response states (loading / success / error) is one of the most common real interview exercises, and reaching for a discriminated union instead of one sprawling optional-fields object is the single strongest signal of TypeScript fluency in that exercise.