Type Guards help you narrow down types to a more specific type than initially declared
type variable
should be understood/known as the specified typeNarrowing asserts/ensures that the function performs the correct action based on specified type and makes your code less error prone
function padLeft(padding: number | string, input: string): string { return " ".repeat(padding) + input; } /** Argument of type 'string | number' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'. */
Because the .repeat()
method exclusively takes number
as a parameter type, initially typed parameter of string | number
doesn't match the type expectation
By narrowing the type down, you can create different behaviors within the function based on the type
function padLeft(padding: number | string, input: string): string { // type of parameter `padding` here is type `number` via narrowing if (typeof padding === "number") { return " ".repeat(padding) + input; } // type of parameter `padding` here is type `string` return padding + input; }
This analysis of code based on reachability is called control flow analysis, and TypeScript uses this flow analysis to narrow types as it encounters type guards and assignments.
Important to note that Typescript is about compile time errors. Since Typescript is compiled to Javascript, there is no runtime type guarantee
Type guards are a function that takes a type
and returns a boolean
, telling TS that type can be arrowed down to something more specific
type
in some scopetypeof
if (typeof a === 'string' && typeof b === 'string') { return a.concat(b); }
Returns a string
based on the type of the value passed
"string", "number", "bigint", "boolean", "symbol", "undefined", "object", "function"
Using if()
, !!
, &&
, ||
, or Boolean()
, your function can guard against falsy
values
What are falsy Values: 0
, NaN
, ""
, 0n
, null
, undefined
function getUsersOnlineMessage(numUsersOnline: number) { if (numUsersOnline) { return `There are ${numUsersOnline} online now!`; } return "Nobody's here. :("; }
Using switch
, ===
, !==
, ==
, or !=
to narrow types
function example(x: string | number, y: string | boolean) { // only time below statement is true is when `x` and `y` are both `string` type if (x === y) { // We can now call 'string' type method on 'x' or 'y'. x.toUpperCase(); y.toLowerCase(); } }
in
Determines if an object or its prototype chain has a property with a given name
type Fish = { swim: () => void }; type Bird = { fly: () => void }; function move(animal: Fish | Bird) { if ("swim" in animal) { return animal.swim(); } return animal.fly(); }
instanceof
Used to check if a value is an instance of a given constructor function or class
function logValue(x: Date | string) { if (x instanceof Date) { console.log(x.toUTCString()); } else { console.log(x.toUpperCase()); } }
See Type Predicate Section