Borislav Hadzhiev
Last updated: Feb 20, 2021
Check out my new book
We use type guards
in typescript when we have logic that is specific to a
particular type.
In other words:
if (typeof someValue === 'string') { // someValue is guaranteed to be of type string // => therefore we can perform all string operations with it // like call someValue.toLowerCase(), etc. }
We want to force typescript to allow us to work with a value as if it's guaranteed to be of type "x". Once we run a type guard, we clarify to typescript the type of value we are working with.
function sum(a: string | number, b: string | number): string | number { // a and b are either of type string or number // therefore we can only use methods on them, // that are common between strings and numbers // i.e. .toString() and .valueOf() if (typeof a === 'string' && typeof b === 'string') { // both a and b are guaranteed to be of type string // can call all string methods on them return a.toUpperCase() + b.toUpperCase(); } if (typeof a === 'number' && typeof b === 'number') { // both a and b are guaranteed to be of type number // can call all number methods on them return a.toFixed(2) + b.toFixed(2); } if (typeof a === 'string' || typeof b === 'string') { // we don't know if a or b is the string, // so we don't get to call string methods on either one of them return String(a) + String(b); } // we have exhausted all the possibilities // Now a and b are guaranteed to be of type 'number' return a.toFixed(2) + b.toFixed(2); }
We often use type guards when working with union types, if we have an either or scenario and we have to narrow down the type of value we're working with to make sure the operations we're trying to perform will be type safe.
In the above example we used typeof
to narrow down the type and use a type
guard, we only use typeof
with primitive values:
For non primitive types we use instanceof
:
function loop(collection: string[] | string): void { // can only access common methods to both // string[] and string if (Array.isArray(collection)) { // collection is guaranteed to be a string[] collection.push('hello world'); } // The above is the same as if (collection instanceof Array) { // collection is guaranteed to be a string[] collection.push('hello world'); } if (typeof collection === 'string') { // collection is guaranteed to be a string console.log(collection.toUpperCase()); } }
Even though typeof would work with an object, it does not work as a type guard
in typescript. For example typeof
an array in javascript would return
"object", but it does not function as a type guard in typescript.
We use type guards anytime we want to narrow down the type of a union type and restore a set of type specific properties on the value.