5 Methods to use Type Guards in TypeScript

Different ways to narrow the type of an imprecise object to a more specific one.

Introduction

Type Guards come into the picture with the usage of TypeScript. This technique is used to identify or narrow down the type of a variable. In case the narrowing is less familiar to you, it means the process of refining a broader type to a more specific one.

For example, a Person’s broader type can narrow to a Teacher, Doctor, Farmer, etc. Mainly, these Type Guards are used inside the conditional blocks. And the Type Guard methods perform in the runtime and return a boolean value.

Why Type Guards

Since the birth of TypeScript, we have been able to write robust code transpiled into optimized vanilla JavaScript. Moreover, as TypeScript is statically typed, it detects common mistakes at compile time and may also avoid some runtime issues. But, can we do better in detecting runtime issues in TypeScript?

Type Guards help detect more runtime failures that the TypeScript framework can not handle. Let’s look at the most influencing Type Guards that come with TypeScript.

“typeof” Type Guard

This keyword is for validating or identifying the basic types in TypeScript or JavaScript. Only the enumeration type adds to TypeScript as an add-on from JavaScript. With the help of typeof keyword, TypeScript can identify the variables declared separately supporting the Types given below.

  • String
  • Boolean
  • Number
  • Undefined
  • Bigint
  • Function

The following code snippet shows you how we can use typeof type guard.

function employeeDetails(emp_: string | number) {
let res_: string = '';
if (typeof emp_ == 'string') {
res_ = Employee name is ${emp_} ;
} else if (typeof emp_ == 'number') {
res_ = Employee age is ${emp_} ;
}
return res_;
}

//first call with a string value
employeeDetails('James');

//second call with a number value
employeeDetails(35);

The method employeeDetails takes a parameter, and inside the method, it checks the type of that parameter value. The first call will return the employee’s name and the second employee’s age. Although this Type Guard is easy to use, it only works for predefined TypeScript types. That is the main limitation of typeof.

“instanceof” Type Guard

instanceof Type Guard can be introduced as an advanced version of the typeof Type Guard. While typeof keyword determines the type of a variable, the operator instanceof will decide whether the given object has properties similar to the specific class or constructor function. Based on that, the operator returns a boolean value.

class Person {
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}

class Animal {
kind: string;
legs: number;
constructor(kind: string, legs: number) {
this.kind = kind;
this.legs = legs;
}
}

function helloWorld(obj_: Person | Animal) {
let res_: string = '';
if (obj_ instanceof Person) {
res_ = `Welcome ${obj_.firstName} ${obj_.lastName}` ;
} else if (obj_ instanceof Animal) {
res_ = `${obj_.kind} has ${obj_.legs} legs` ;
}
return res_;
}

const james = new Person("James", "Anderson");
const jimmy = new Animal("Dog", 4);

//first call with a person
helloWorld(james);

//second call with an animal
helloWorld(jimmy);

The above code has two classes(Person, Animal) and one function called helloWorld(). The function takes one parameter, and inside the method, it gets validated with the instanceof operator. The first call returns the person’s name, and the second, the number of legs the animal has.

Unlike the typeof operator, the instanceof Type Guard can only be used within a conditional block. It returns only a boolean value. To find the type of imprecise variable, we need to compare it with the known classes or constructor function types.

Keyword “in” as a Type Guard

Again, this is a Type Guard that we can use to differentiate types from one another. The in keyword checks if the selected object contains the given properties. According to that condition, it will return a boolean value. This keyword will be helpful for functional validations and avoiding runtime issues.

interface Animal {
group : string;
}

class Fish implements Animal {
group : string;
finsColor : string;
constructor(finsColor: string, group: string) {
this.finsColor = finsColor;
this.group = group;
}
}

class Bird implements Animal {
group : string
feathersColor : string;
constructor(feathersColor: string, group: string){
this.feathersColor = feathersColor;
this.group = group;
}
}

function swim(group : string) {
console.log(${group} can swim! );
}
function fly(group : string) {
console.log(${group} can fly! );
}

function moving(animal : Animal) {
if ('finsColor' in animal) {
swim((animal as Fish).group);
}
else if ('feathersColor' in animal) {
fly((animal as Bird).group);
}
}

//first call with a bird
moving(new Bird('green', 'bird'));
//second call with a fish
moving(new Fish('gold', 'fish'));

The above code contains two Classes that implement the Animal Interface. The moving method examines the properties of the received animal object. The first call invokes the fly() method since the Animal has the ‘feathersColor’ property belonging to the Bird Class. Similarly, the second call goes to the swim() method because it finds properties in the Fish Class.

“Equality narrowing” Type Guard

Equality Narrowing returns a value of an expression which will be a boolean value. The expression returns as true if the values and squares of both variables are the same. Otherwise, it will return false. This Type Guard helps identify the imprecise type variable. For example, suppose one variable is an imprecise type(i.e., unknown, any, etc.) and the other is a precise type (i.e., number, string, etc.). In that case, TypeScript uses that information to narrow the type of the first variable.

function getValues(a: number | string, b: string) {
if(a === b) {
// this is where the narrowing takes place. narrowed to string
console.log(typeof a) // string
} else {
// if there is no narrowing, type remains unknown
console.log(typeof a) // number or string
}
}

//first call with number and string value
getValues(10, '10')
//second call with two string values
getValues('10', '10')

The above function takes two parameters. The first parameter is imprecise since we can not guarantee whether it is a number or string. However, the second parameter is a string value, so we have used it to narrow down the first variable’s type.

Build a custom Type Guard

TypeScript has a limited number of predefined Type Guards. However, we can also create custom Type Guards. Here is an example of a custom Type Guard to find out whether the Animal is a Bird or a Fish.

interface IBird{
name: string;
feathersColor: string;
}

interface IFish{
name: string;
finsColor: string;
}

type Animal = IBird | IFish;

/** Custom Type Guard */
const isBird = (animal: Animal): animal is IBird => {
let res_ = (animal as IBird).feathersColor !== undefined;
return res_;
}

const parrot: Animal = {name: "Parrot", feathersColor: "green"};
const oscar: Animal = {name: "Oscar", finsColor: "orange"};

//first call with a bird
console.log(isBird(parrot))
//second call with a fish
console.log(isBird(oscar))

Since even the most minor mistakes we make when creating our type guard can lead to multiple errors, accuracy is the most important thing to consider.

Build composable web applications

Don’t build web monoliths. Use Bit to create and compose decoupled software components — in your favorite frameworks like React or Node. Build scalable and modular applications with a powerful and enjoyable dev experience.

Bring your team to Bit Cloud to host and collaborate on components together, and speed up, scale, and standardize development as a team. Try composable frontends with a Design System or Micro Frontends, or explore the composable backend with serverside components.

Give it a try →

Learn more


5 Methods to use Type Guards in TypeScript was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Kavindu Gunathilake

Different ways to narrow the type of an imprecise object to a more specific one.

Introduction

Type Guards come into the picture with the usage of TypeScript. This technique is used to identify or narrow down the type of a variable. In case the narrowing is less familiar to you, it means the process of refining a broader type to a more specific one.

For example, a Person’s broader type can narrow to a Teacher, Doctor, Farmer, etc. Mainly, these Type Guards are used inside the conditional blocks. And the Type Guard methods perform in the runtime and return a boolean value.

Why Type Guards

Since the birth of TypeScript, we have been able to write robust code transpiled into optimized vanilla JavaScript. Moreover, as TypeScript is statically typed, it detects common mistakes at compile time and may also avoid some runtime issues. But, can we do better in detecting runtime issues in TypeScript?

Type Guards help detect more runtime failures that the TypeScript framework can not handle. Let’s look at the most influencing Type Guards that come with TypeScript.

"typeof" Type Guard

This keyword is for validating or identifying the basic types in TypeScript or JavaScript. Only the enumeration type adds to TypeScript as an add-on from JavaScript. With the help of typeof keyword, TypeScript can identify the variables declared separately supporting the Types given below.

  • String
  • Boolean
  • Number
  • Undefined
  • Bigint
  • Function

The following code snippet shows you how we can use typeof type guard.

function employeeDetails(emp_: string | number) {
let res_: string = '';
if (typeof emp_ == 'string') {
res_ = Employee name is ${emp_} ;
} else if (typeof emp_ == 'number') {
res_ = Employee age is ${emp_} ;
}
return res_;
}

//first call with a string value
employeeDetails('James');

//second call with a number value
employeeDetails(35);

The method employeeDetails takes a parameter, and inside the method, it checks the type of that parameter value. The first call will return the employee's name and the second employee's age. Although this Type Guard is easy to use, it only works for predefined TypeScript types. That is the main limitation of typeof.

"instanceof" Type Guard

instanceof Type Guard can be introduced as an advanced version of the typeof Type Guard. While typeof keyword determines the type of a variable, the operator instanceof will decide whether the given object has properties similar to the specific class or constructor function. Based on that, the operator returns a boolean value.

class Person {
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}

class Animal {
kind: string;
legs: number;
constructor(kind: string, legs: number) {
this.kind = kind;
this.legs = legs;
}
}

function helloWorld(obj_: Person | Animal) {
let res_: string = '';
if (obj_ instanceof Person) {
res_ = `Welcome ${obj_.firstName} ${obj_.lastName}` ;
} else if (obj_ instanceof Animal) {
res_ = `${obj_.kind} has ${obj_.legs} legs` ;
}
return res_;
}

const james = new Person("James", "Anderson");
const jimmy = new Animal("Dog", 4);

//first call with a person
helloWorld(james);

//second call with an animal
helloWorld(jimmy);

The above code has two classes(Person, Animal) and one function called helloWorld(). The function takes one parameter, and inside the method, it gets validated with the instanceof operator. The first call returns the person's name, and the second, the number of legs the animal has.

Unlike the typeof operator, the instanceof Type Guard can only be used within a conditional block. It returns only a boolean value. To find the type of imprecise variable, we need to compare it with the known classes or constructor function types.

Keyword "in" as a Type Guard

Again, this is a Type Guard that we can use to differentiate types from one another. The in keyword checks if the selected object contains the given properties. According to that condition, it will return a boolean value. This keyword will be helpful for functional validations and avoiding runtime issues.

interface Animal {
group : string;
}

class Fish implements Animal {
group : string;
finsColor : string;
constructor(finsColor: string, group: string) {
this.finsColor = finsColor;
this.group = group;
}
}

class Bird implements Animal {
group : string
feathersColor : string;
constructor(feathersColor: string, group: string){
this.feathersColor = feathersColor;
this.group = group;
}
}

function swim(group : string) {
console.log(${group} can swim! );
}
function fly(group : string) {
console.log(${group} can fly! );
}

function moving(animal : Animal) {
if ('finsColor' in animal) {
swim((animal as Fish).group);
}
else if ('feathersColor' in animal) {
fly((animal as Bird).group);
}
}

//first call with a bird
moving(new Bird('green', 'bird'));
//second call with a fish
moving(new Fish('gold', 'fish'));

The above code contains two Classes that implement the Animal Interface. The moving method examines the properties of the received animal object. The first call invokes the fly() method since the Animal has the 'feathersColor' property belonging to the Bird Class. Similarly, the second call goes to the swim() method because it finds properties in the Fish Class.

“Equality narrowing” Type Guard

Equality Narrowing returns a value of an expression which will be a boolean value. The expression returns as true if the values and squares of both variables are the same. Otherwise, it will return false. This Type Guard helps identify the imprecise type variable. For example, suppose one variable is an imprecise type(i.e., unknown, any, etc.) and the other is a precise type (i.e., number, string, etc.). In that case, TypeScript uses that information to narrow the type of the first variable.

function getValues(a: number | string, b: string) {
if(a === b) {
// this is where the narrowing takes place. narrowed to string
console.log(typeof a) // string
} else {
// if there is no narrowing, type remains unknown
console.log(typeof a) // number or string
}
}

//first call with number and string value
getValues(10, '10')
//second call with two string values
getValues('10', '10')

The above function takes two parameters. The first parameter is imprecise since we can not guarantee whether it is a number or string. However, the second parameter is a string value, so we have used it to narrow down the first variable’s type.

Build a custom Type Guard

TypeScript has a limited number of predefined Type Guards. However, we can also create custom Type Guards. Here is an example of a custom Type Guard to find out whether the Animal is a Bird or a Fish.

interface IBird{
name: string;
feathersColor: string;
}

interface IFish{
name: string;
finsColor: string;
}

type Animal = IBird | IFish;

/** Custom Type Guard */
const isBird = (animal: Animal): animal is IBird => {
let res_ = (animal as IBird).feathersColor !== undefined;
return res_;
}

const parrot: Animal = {name: "Parrot", feathersColor: "green"};
const oscar: Animal = {name: "Oscar", finsColor: "orange"};

//first call with a bird
console.log(isBird(parrot))
//second call with a fish
console.log(isBird(oscar))

Since even the most minor mistakes we make when creating our type guard can lead to multiple errors, accuracy is the most important thing to consider.

Build composable web applications

Don’t build web monoliths. Use Bit to create and compose decoupled software components — in your favorite frameworks like React or Node. Build scalable and modular applications with a powerful and enjoyable dev experience.

Bring your team to Bit Cloud to host and collaborate on components together, and speed up, scale, and standardize development as a team. Try composable frontends with a Design System or Micro Frontends, or explore the composable backend with serverside components.

Give it a try →

Learn more


5 Methods to use Type Guards in TypeScript was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Kavindu Gunathilake


Print Share Comment Cite Upload Translate Updates
APA

Kavindu Gunathilake | Sciencx (2022-06-08T07:47:50+00:00) 5 Methods to use Type Guards in TypeScript. Retrieved from https://www.scien.cx/2022/06/08/5-methods-to-use-type-guards-in-typescript/

MLA
" » 5 Methods to use Type Guards in TypeScript." Kavindu Gunathilake | Sciencx - Wednesday June 8, 2022, https://www.scien.cx/2022/06/08/5-methods-to-use-type-guards-in-typescript/
HARVARD
Kavindu Gunathilake | Sciencx Wednesday June 8, 2022 » 5 Methods to use Type Guards in TypeScript., viewed ,<https://www.scien.cx/2022/06/08/5-methods-to-use-type-guards-in-typescript/>
VANCOUVER
Kavindu Gunathilake | Sciencx - » 5 Methods to use Type Guards in TypeScript. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/06/08/5-methods-to-use-type-guards-in-typescript/
CHICAGO
" » 5 Methods to use Type Guards in TypeScript." Kavindu Gunathilake | Sciencx - Accessed . https://www.scien.cx/2022/06/08/5-methods-to-use-type-guards-in-typescript/
IEEE
" » 5 Methods to use Type Guards in TypeScript." Kavindu Gunathilake | Sciencx [Online]. Available: https://www.scien.cx/2022/06/08/5-methods-to-use-type-guards-in-typescript/. [Accessed: ]
rf:citation
» 5 Methods to use Type Guards in TypeScript | Kavindu Gunathilake | Sciencx | https://www.scien.cx/2022/06/08/5-methods-to-use-type-guards-in-typescript/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.