TypeScript: How Type Guards Narrow Union Types
Type guards are runtime checks that teach TypeScript about your types, allowing it to 'narrow' a broad union type. This lets you use type-specific methods safely. Without a guard, TypeScript will error because it can't guarantee the type is correct.
WHY IT EXISTS When you have a variable that can be one of several types (a union type, like string | number), you can't perform an operation that's only valid for one of those types. The compiler will stop you because it can't guarantee the variable is the correct type at that moment, even if your logic seems sound.
THE MENTAL MODEL Think of type guards as checkpoints in your code's logic. When your code passes a check like typeof myVar === "string", you're not just controlling the runtime flow; you're also giving a hint to the TypeScript compiler. Inside that if block, TypeScript "narrows" its understanding of myVar from a general string | number to a specific string.
HOW IT WORKS TypeScript analyzes your program's control flow. It recognizes specific patterns of code as type guards. The most common guard is the typeof check for primitive types. When TypeScript sees if (typeof padding === "number"), it knows that for the entire scope of that if block, the type of padding is number. In an else block, it would infer the remaining possible types from the union. This process of refining types based on control flow is called narrowing.
WHEN TO USE IT Use narrowing any time you have a variable with a union type and need to perform an operation specific to one of the types in that union. It's fundamental to writing safe and idiomatic TypeScript when dealing with values that can have multiple forms, such as flexible function arguments or data from an API response.
WHEN NOT TO USE IT Narrowing isn't needed if a variable's type is already specific enough for your operation. If a function only accepts padding: number, no guard is necessary. Also, while typeof works for primitives, it is less useful for complex objects (where it just returns "object"). For those, you need other techniques like instanceof checks for classes or custom user-defined type guards.
ONE CANONICAL EXAMPLE A function padLeft(padding: number | string, input: string) needs to behave differently based on the type of padding. A direct call to a number-only method like " ".repeat(padding) will cause a compile-time error. The solution is to wrap it in a type guard: if (typeof padding === "number") { return " ".repeat(padding) + input; }. Inside this block, padding is known to be a number. The else part can then safely handle it as a string: return padding + input;.
Read the original → typescriptlang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.