Reading — step 1 of 5
Learn
~1 min readGenerics and Unions
Union types describe values that could be one of several:
let id: string | number;
id = 42;
id = "abc";
To USE a union, you must narrow — convince the compiler which case you're in:
function stringify(id: string | number): string {
if (typeof id === "string") {
return id.toUpperCase(); // narrowed to string
}
return id.toFixed(0); // narrowed to number
}
Narrowing patterns:
typeof x === "string"— for primitivesx instanceof Class— for classes"prop" in x— does the value have this property?Array.isArray(x)if (x !== null)— narrows away null/undefined- Discriminated unions — see below
Discriminated unions are the killer pattern. Each variant has a unique tag:
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number }
| { kind: "rect"; width: number; height: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2; // s narrowed to circle
case "square": return s.side ** 2;
case "rect": return s.width * s.height;
}
}
The compiler checks exhaustiveness — add a new variant and all switch blocks light up.
Exhaustiveness pattern with never:
function area(s: Shape): number {
switch (s.kind) {
case "circle": ...;
// omit a case to see the error
default:
const _exhaustive: never = s; // compile error if Shape isn't fully covered
return _exhaustive;
}
}
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…