The 'typeof' type guard
Consider this example:
- We have a function that prints an input to the console
- The input could be a 'string', or an 'Array of strings'
ts
functionprintAll (text : string | string[]) {// print each partProperty 'forEach' does not exist on type 'string | string[]'. Property 'forEach' does not exist on type 'string'.2339Property 'forEach' does not exist on type 'string | string[]'. Property 'forEach' does not exist on type 'string'.text .(( forEach str : string) => {console .log (str );});}
Here typescript is warning us that forEach
is available on the array string[]
but not on the single string
We can fix this by checking the typeof text
before proceeding:
ts
functionprintAll (text : string | string[]) {if (typeoftext === "object") {// text must be string[] since Arrays are objects in JS// print each parttext .forEach ((str : string) => {console .log (str );});} else {// if not array, must be a string, this is narrowingconsole .log (text );}}
Let's examine this code
-
When TypeScript encounters
typeof text === "object"
inside outif
statement, it understands that as a type guard. -
This process of refining types to a more specific type than originally declared is called narrowing.
When it reaches the else
statement it knows by elimination that text
is a string
, since we already dealt with the array string[]
earlier, and string
is the only other type we declared as a possible input when we declared the function:
function printAll(text: string | string[]) {
The 'typeof' operator
JavaScript supports a typeof
operator which can give us information about the type of a value.
It returns a string from one of the following:
"string"
"number"
"bigint"
"boolean"
"symbol"
"undefined"
"object"
"function"
Note:
null
andarrays
are a type of object in JavaScript
We can use these values in our type guards to narrow down the possible values of an input and work with them appropriately.
Front-End Example:
Let's create an example where we handle different types of input for a width setting.
The user can provide the width either as a string with a unit (like "100px"
or "50%"
) or as a number representing pixels.
We'll use the typeof
type guard to manage these cases.
ts
functionsetElementWidth (element :HTMLElement ,width : string | number) {if (typeofwidth === "string") {// If width is a string,// assume it's in a valid CSS format (e.g., "100px" or "50%")element .style .width =width ;} else if (typeofwidth === "number") {// If width is a number, treat it as pixelselement .style .width = `${width }px`;}}// usage:constelement =document .getElementById ("my-element")!;setElementWidth (element , "30%"); // Sets width to 30%setElementWidth (element , 100); // Sets width to 100px