Narrowing in TypeScript
Narrowing refers to the process of reducing the type of a variable from a broader type to a more specific type within a certain code block or context.
TypeScript variables or functions can have types which is Union of multiple types. In order for TypeScript to refine the type checking and predict accurate types, we need to help TypeScript by placing some conditional statements called Type Guards and this process of refining the types is called Narrowing. Type Guars help the TypeScript compiler understand precisely what the type of a variable is at a given point in the code. Narrowing refers to the process of reducing the type of a variable from a broader type to a more specific type within a certain code block or context.
Let's look at an example from official TypeScript documentation.
function padLeft(padding: number | string, input: string): string {
}
This function takes two parameters , first parameter is number of spaces or padding on the left side of the text which is in second parameter. First parameter has a union type or number or string. If it was a string means it contains spaces and if it was a number, we need to add that many spaces on the left side of the input parameter. Let's try to implement this.
function padLeft(padding: number | string, input: string): string {
return " ".repeat(padding) + input;
}
Here in this case TypeScript will give us an error.
Argument of type 'string | number' is not assignable to parameter of type 'number'.
Type 'string' is not assignable to type 'number'.
We need to use a Type Guard in order to get the actual expected type which is string.
So we can re-write our code to something like this.
function padLeft(padding: number | string, input: string): string {
if (typeof padding === "number") {
return " ".repeat(padding) + input;
}
return padding + input;
}
Here TypeScript sees typeof padding === "number"
as a Type Guard. TypeScript follows possible paths of execution that our programs can take to analyze the most specific possible type of a value at a given position. It looks at these special checks (Type Guards) and assignments. In many code editors you can observe these types as they change from one type to another in these blocks of codes.
Here are some common techniques TypeScript uses to narrow down the possible types.
typeOf
Type Guards
typeOf
operator can help TypeScript to get the type of a variable if it is one of the following basic types, including string, number , bigint, boolean, symbol, undefined, object and function.
instanceOf
Narrowing
instanceOf
operator helps TypeScript to understand if an object is an instance of a particular class.
Truthiness
Typescript make use of statements which result in true/false values to predict the types. Statements like &&
, ||
, !
can help TypeScript make these decisions.
Equality Narrowing
TypeScript also uses switch
statements and equality checks like ===
, !==
, ==
, and !=
to narrow types.
in
operator Narrowing
JavaScript in
operator can also be used to determine if an object or its prototype chain has a property with a name. TypeScript takes this into account as a way to narrow down potential types.
Assignment Operators
TypeScript also used the assignment operator =
to determine the type of a variable. TypeScript looks at the right side of the assignment and narrows the left side appropriately.
Control Flow Analysis
Control statement like if
and while
also help TypeScript determine the accurate types in certain conditional code blocks.
Type Predicates
Type predicates like test is string
also called user defined Type Guards. TypeScript use these statements to narrow down the types.
There is lot more techniques used by TypeScript to determine the most accurate types during the compile time. Check out the official TypeScript documentation for detailed information with examples on above mentioned and other ways it used to narrow down the types.