Union Types
A task id arrives from a form as the string "7", and from the database as the number 7. Your function should accept both. One type cannot describe that; two types joined together can.
The rule
A | B means either one; use only what both share until you narrow.
A union type lists alternatives separated by |. A value of the union may be any one of them:
function normaliseId(id: number | string): number {
if (typeof id === "string") {
return Number(id.trim()); // here id is a string
}
return id; // here id is a number
}
normaliseId(7); // fine
normaliseId("7"); // fine
normaliseId(true); // error: boolean is not in the unionThe key rule is what you may do with a union value before you check it. TypeScript only allows operations valid for every member. id.toString() works, because both numbers and strings have it. id.trim() does not, because numbers have no trim.
Narrowing is how you get from "either" to "this one". A check such as typeof id === "string" tells the compiler which member you have inside that branch, and afterwards it knows the other member remains. Other checks narrow too: === undefined, Array.isArray(x), x instanceof Date, or "title" in x.
Unions of literal types are one of the most useful patterns in TypeScript:
type Status = "todo" | "doing" | "done";
let s: Status = "doing";
s = "finished"; // errorWith object types, give each member a shared tag property and you get a discriminated union, which narrows cleanly on that tag:
type Result =
| { ok: true; task: Task }
| { ok: false; error: string };
function show(r: Result) {
if (r.ok) { console.log(r.task.title); }
else { console.log(r.error); }
}The shortcut
Treat a union like a box you have not opened. Ask "Which member do I have here?" If you do not know yet, use only what every member has. If you need something specific, open the box with a check first.
The type
due: string | Date— some tasks store text, others a Date object.
Drills
Answer fast — trust the rule.
Which value is NOT allowed for a variable of type number | string?
id has type number | string. Which call compiles without any check first?
Inside if (typeof id === "string") { ... }, what is the type of id?
For type Result with members tagged ok: true and ok: false, how do you reach error safely?
Remember
- A union means either one; use only what every member shares until you narrow.
- Ask "which member do I have here?" and narrow with typeof, instanceof, in, or a tag check.
- Trap: calling a member-specific method on an unnarrowed union.
