A union describes alternatives. string | number means a value may be a string or a number. It does not mean both at once, and it does not perform conversion. Before using an operation that works on only one alternative, your code must establish which one it has.
A literal type names an exact value, such as “planned”. A union of literals creates a small vocabulary: type Status = "planned" | "done". This catches misspellings at checked boundaries and makes supported states visible to readers.
Use literal unions for categories with distinct meanings. A boolean is fine for a true/false fact, but a status with planned, active and done has three meaningful possibilities. Do not encode it using unexplained numeric values.
A parameter annotated with Status accepts either allowed literal. When creating object records, annotate the intended record type if a property’s inferred type widens to string. The checker needs evidence that a changing string still belongs to the allowed set; it cannot assume that because you know it informally.
A type with many alternatives is not automatically a better model. Include only the states the application needs. At runtime a union has disappeared, so unknown text from outside the program still requires validation. Day 6 will make that boundary explicit.
Predict, check, then run
Before running, write down the output and explain which information the checker knows. Click Check types first, then Check & run.
type Status = "planned" | "done";
function badge(status: Status): string {
return status === "done" ? "Complete" : "Next";
}
console.log(badge("planned"));
console.log(badge("done"));Enable JavaScript to edit and type-check this example.
Check your prediction
Next
Complete Read a real compiler error
The value is a string, but is it one of the allowed strings?
type Status = "planned" | "done";
const status: Status = "finished";
console.log(status);Enable JavaScript to edit and type-check this example.
Check your diagnosis
The literal “finished” is outside Status. Use “done” if it means the same thing. Add another alternative only when it represents a real state with defined behavior.
A const record can still have changing fields
A const binding prevents rebinding the variable; it does not freeze the record. In const task = { status: "planned" }, the status property usually widens to string because it can be changed. A string could contain an unsupported state, so it cannot automatically be passed to a Status parameter. Annotate the record’s intended shape when its fields have a restricted vocabulary.
type Status = "planned" | "done";
type Task = {
topic: string;
status: Status;
};
function badge(status: Status): string {
return status === "done" ? "Complete" : "Next";
}
const task: Task = { topic: "Types", status: "planned" };
console.log(badge(task.status));
task.status = "done";
console.log(badge(task.status));Enable JavaScript to edit and type-check this example.
Check your prediction
Next
Complete Model a finite set of meaningful alternatives
A union describes alternatives; it does not convert one into another. A parameter accepting string|number must handle either kind. A literal union can instead restrict a value to a closed vocabulary such as planned or done.
Use a closed set when spelling is part of the domain contract. A broad string accepts accidental misspellings that a literal union rejects. The compiler still cannot prove which state should apply at runtime; your transition code chooses it.
type Status = "planned" | "done";
function next(status: Status): Status {
return status === "planned" ? "done" : "planned";
}
console.log(next("planned"));
console.log(next("done"));Enable JavaScript to edit and type-check this example.
Check your prediction
done
planned Both return branches belong to the declared vocabulary. The function implements a toggle; if the intended rule were one-way completion, this type alone would not catch the behavioral mismatch.
function label(value: string | number): string {
return typeof value === "number" ? `${value} min` : value.trim();
}
console.log(label(0));
console.log(label(" Review "));Enable JavaScript to edit and type-check this example.
Check your prediction
0 min
Review The typeof branch supplies the evidence for the number-specific behavior. A union is not permission to call methods supported by only one of its members without checking first.
Try the rule on new inputs
Try a misspelled status and explain the rejected assignment. Then add a new legitimate status and identify every function whose behavior must change, even if its current type still compiles.
Your turn
Union & Literal Types: practise
Define Priority as “low” | “high”. Write priorityLabel returning “Do soon” for high and “Later” for low. Check both outputs.
// Define the union and a typed function. Use if/else or a conditional expression.Enable JavaScript to edit and type-check this example.
Hint 1: the idea
The parameter describes the vocabulary your function accepts.
Hint 2: a first step
Compare the priority with the exact literal “high”.
Show one solution
type Priority = "low" | "high";
function priorityLabel(priority: Priority): string {
return priority === "high" ? "Do soon" : "Later";
}
console.log(priorityLabel("high"));
console.log(priorityLabel("low"));Enable JavaScript to edit and type-check this example.
Do soon
LaterExplain the types at the function boundary and the runtime checks, if any. Compare your approach before copying this answer.
Change it and explain it
Try passing “urgent”. Should you expand the union or fix the caller? Answer based on the application’s vocabulary. Then add a third priority deliberately and update the function so it has a meaningful label.
Completion is saved in this browser only. It does not measure mastery.