Basic Type Guards
When a variable can be one of several types (a Union), you cannot safely use properties that only exist on some of those types. TypeScript forces you to narrow the type first.
The mental model
Type narrowing is the process of removing possibilities from a union type until TypeScript is certain of the exact type. You do this using standard JavaScript control flow constructs — typeof, instanceof, and truthiness checks. TypeScript watches these checks and narrows the type inside the corresponding block.
function processValue(val: string | number) {
// val.toUpperCase(); // Error: Property 'toUpperCase' does not exist on type 'number'
if (typeof val === "string") {
// TypeScript knows val is exactly 'string' here
return val.toUpperCase();
}
// TypeScript knows val MUST be 'number' here, since 'string' returned early
return val.toFixed(2);
}
Truthiness narrowing
Checking for truthiness is the most common way to eliminate null or undefined from a union.
function printUser(name: string | null) {
if (!name) {
return; // name is null or an empty string
}
// name is guaranteed to be a string here
console.log(name.toLowerCase());
}
The in operator for objects
When you have a union of custom objects, typeof won't help because both will return "object". Use the in operator to check if a specific property exists on the object.
type MouseEvent = { x: number, y: number };
type KeyboardEvent = { key: string };
function handleEvent(event: MouseEvent | KeyboardEvent) {
if ("x" in event) {
// event is narrowed to MouseEvent
console.log(event.x);
} else {
// event is narrowed to KeyboardEvent
console.log(event.key);
}
}
Type guards are just standard JavaScript checks (
typeof,in,instanceof, truthiness) that TypeScript understands statically to narrow down union types.