TypeScriptUnions and narrowing
Type narrowing
Determining a more precise type for a value based on checks in your code.
Example
#TypeScript
function total(input: number | number[] | null): number {
if (input === null) return 0
if (Array.isArray(input)) return input.reduce((sum, n) => sum + n, 0)
return input
}
console.log(total(null))
console.log(total(7))
console.log(total([3, 4, 5]))Result
0 7 12
Definition and usage
#Narrowing is the mechanism that lets TypeScript know more about a value, within a given block of code, than its declaration alone states. After a check like if (typeof value === "string"), inside that block the variable has the type string, even if it was a union before.
The compiler understands many kinds of checks: typeof, instanceof, the in operator, comparisons with literals, checks against null and undefined, Array.isArray(), and your own type guard functions.
It also works the other way around: after a return in one branch, TypeScript knows that case no longer applies in the rest of the function.
Syntax
#Syntax
if (typeof value === "string") {
// value: string
}Good practices
#- An early
returnfor edge cases (null, an empty array) simplifies the rest of the function, because the type is already narrowed. - When a check gets complex, extract it into a type guard function with the type
value is T.
Related entries
#- Type guards (value is T)Custom checking functions that narrow the type at the call site.
- typeof (in types)Gets the type of a variable or value so you can use it somewhere else.
- instanceofNarrows the type to a class by checking whether the object was created by it.
- The in operatorNarrows a union of objects by checking whether an object has a given property.
- Union (A | B)A type that can be one of several types.
Spotted a mistake or missing an example? Write to us.