Careeroza
Unions with null and Narrowing
Optional Values
The most common union is T | null or T | undefined. TypeScript narrows the type after a check, so inside the if block it is no longer nullable.
Example
type User = { name: string; coupon: string | null };
function checkoutMessage(user: User): string {
if (user.coupon === null) {
return user.name + ", no coupon applied";
}
return user.name + ", coupon " + user.coupon.toUpperCase() + " applied";
}
console.log(checkoutMessage({ name: "Asha", coupon: null }));
console.log(checkoutMessage({ name: "Ravi", coupon: "save10" }));Output
Asha, no coupon applied Ravi, coupon SAVE10 applied
Literal Unions as Tags
Example
When each object in a union has a common literal field like kind, a switch on that field narrows perfectly. This pattern (a discriminated union) is covered in depth in the medium level.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rect"; width: number; height: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "rect": return s.width * s.height;
}
}
console.log(area({ kind: "circle", radius: 1 }).toFixed(2));
console.log(area({ kind: "rect", width: 3, height: 4 }));Output
3.14 12
Practice
Exercise: Write a type Theme = "light" | "dark" | "system" and a function nextTheme(t: Theme): Theme that cycles light -> dark -> system -> light.
Show answer
type Theme = "light" | "dark" | "system";
function nextTheme(t: Theme): Theme {
if (t === "light") return "dark";
if (t === "dark") return "system";
return "light";
}
console.log(nextTheme("light"), nextTheme("system"));