Type narrowing in TypeScript is the process of refining the type information of a variable based on checks or assertions performed in the runtime code. It helps the TypeScript compiler to further understand the type of a variable at a given point in the code, allowing more precise type checking and autocompletion for that variable.
Type narrowing can be achieved using various constructs in TypeScript, such as type guards, custom type guards, type predicates, discriminated unions, and ‘as‘ or ‘!‘ type assertions.
Let me explain with examples. Consider the following union type:
type Shape = Circle | Rectangle;
Now let’s say we have a variable ‘shape‘ of type ‘Shape‘. Initially, TypeScript only knows that ‘shape‘ is either a ‘Circle‘ or a ‘Rectangle‘.
const shape: Shape = getShape();
We can use type narrowing to further refine this type information, for example with a type guard using an ‘if‘ statement.
if ("radius" in shape) {
// Inside this block, TypeScript knows that 'shape' is of type 'Circle'
} else {
// Inside this block, TypeScript knows that 'shape' is of type 'Rectangle'
}
Here’s another example using discriminated unions. Suppose we have an ‘Animal‘ type represented by a discriminated union of ‘Dog‘ and ‘Cat‘, differentiating them by the ‘kind‘ property:
type Dog = {
kind: "dog";
bark: () => void;
};
type Cat = {
kind: "cat";
meow: () => void;
};
type Animal = Dog | Cat;
We can use type narrowing with a switch statement:
function handleAnimal(animal: Animal) {
switch (animal.kind) {
case "dog":
// TypeScript knows 'animal' is of type 'Dog' in this case
animal.bark();
break;
case "cat":
// TypeScript knows 'animal' is of type 'Cat' in this case
animal.meow();
break;
}
}
You can illustrate type narrowing with the following flowchart:
The code above can be represented like this:
$$\begin{cases}
\mathit{shape} \xrightarrow{\text{"radius" in shape?}} & \text{Yes} \Longrightarrow \text{"shape" is narrowed to "Circle"} \\
\mathit{shape} \xrightarrow{\text{"radius" in shape?}} & \text{No} \Longrightarrow \text{"shape" is narrowed to "Rectangle"}
\end{cases}$$
To summarize, type narrowing is the process of refining the type information of a variable in TypeScript, based on runtime checks or assertions. This allows the TypeScript compiler to provide more precise type checking and code suggestions for that variable.