TypeScript Union Types and Narrowing
Accept more than one type and narrow it safely with typeof.
On this page
TypeScript type annotations and if statements.
- Accept more than one type and narrow it safely with typeof.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
A union type such as number | string says a value may be any one of the listed types. You can only use operations that are valid for every member until you narrow the type.
Inside an if that tests typeof id === "number", TypeScript knows id is a number, and in the rest of the function it knows id is a string. This is called narrowing, and it lets one function handle several kinds of input without casts.
Code example
type Id = number | string;
function format(id: Id): string {
if (typeof id === "number") {
return `#${id.toFixed(0)}`;
}
return id.toUpperCase();
}
console.log(format(42));
console.log(format("ab7"));#42 AB7
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
type Id = number | string; function format(id: Id): string { if (typeof id === "number") { return `#${id.toFixed(0)}`; } return id.toUpperCase();} console.log(format(42));console.log(format("ab7"));Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| Id can hold either a number or a string. | — | — | |
| Test the runtime type; inside this block id is a number. | — | — | |
| 42 is a number, so the first branch formats it. | id = 42 | #42 | |
| The string reaches the last line, where id is known to be a string. | id = "ab7" | #42 AB7 |
Behind the scenes
How TypeScript processes this code
TypeScript checks types and emits JavaScript. Type annotations are removed before execution.
Cost and common mistakes
Narrowing is checked at compile time. Each call is O(1).
- Calling toUpperCase on the whole union is a compile error; narrow first.
- typeof null is "object", so narrow null and undefined with explicit checks.
Try it yourself
Predict format(7.9).
Reveal answer and reasoning
toFixed(0) rounds to "8", so it prints #8.