Type Narrowing
Whenever you handle variables classified as Unions (string | number) or completely mysterious Types (unknown), TypeScript will lock you out from utilizing specific methods.
Before we can use .toUpperCase() on a Union natively, we must write logical Type Guards to systematically eliminate alternative potential outcomes. This process is called Type Narrowing.
1. The typeof Guard
The quickest way to structurally narrow primitive types natively.
function processLength(input: string | number[]) {
// At this point, TS doesn't know if 'input' is a string or an Array.
// NARROWING:
if (typeof input === "string") {
// Inside this specific block, TS mathematically guarantees 'input' is a string!
return input.length;
} else {
// Since it wasn't a string, TS logically infers it MUST be an array smoothly here.
return input.length;
}
}
2. Truthiness Narrowing
Checking if a value inherently exists to eliminate null or undefined variants perfectly.
function printEmails(emails: string[] | null) {
// If 'emails' is null, it evaluates heavily as falsy cleanly!
if (!emails) {
console.log("No emails found!");
return;
}
// Down here, TS successfully dropped 'null' from the Union cleanly.
emails.forEach(e => console.log(e));
}
3. The instanceof Guard
Used gracefully to narrow downward into specialized instantiated Class logical objects dynamically.
class Car { drive() {} }
class Boat { float() {} }
function operateVehicle(vehicle: Car | Boat) {
if (vehicle instanceof Boat) {
vehicle.float();
} else {
vehicle.drive();
}
}