TypeScript Advanced Types — Type Guards
Tags: typescript, javascript, programming, webdev
Published: 2021-01-23 19:58:00

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

TypeScript has many advanced type capabilities which make writing dynamically typed code easy. It also facilitates the adoption of existing JavaScript code since it lets us keep the dynamic capabilities of JavaScript while using the type-checking capabilities of TypeScript.

There are multiple kinds of advanced types in TypeScript, like intersection types, union types, type guards, nullable types, and type aliases, and more. In this article, we’ll look at type guards.

* * *

Type Guards
===========

To check if an object is of a certain type, we can make our own type guards to check for members that we expect to be present and the data type of the values. To do this, we can use some TypeScript-specific operators and also JavaScript operators.

One way to check for types is to explicitly cast an object with a type with the `as` operator. This is needed for accessing a property that’s not specified in all the types that form a union type.

For example, if we have the following code:

```

interface Person {
  name: string;
  age: number;
}
interface Employee {
  employeeCode: string;
}
let person: Person | Employee = {
  name: 'Jane',
  age: 20,
  employeeCode: '123'
};
console.log(person.name);
```

Then the TypeScript compiler won’t let us access the `name` property of the `person` object since it’s only available in the `Person` type but not in the `Employee` type. Therefore, we’ll get the following error:

```
Property 'name' does not exist on type 'Person | Employee'.Property 'name' does not exist on type 'Employee'.(2339)
```

In this case, we have to use the type assertion operator available in TypeScript to cast the type to the `Person` object so that we can access the `name` property, which we know exists in the `person` object.

To do this, we use the `as` operator, as we do in the following code:

```
interface Person {
  name: string;
  age: number;
}
interface Employee {
  employeeCode: string;
}
let person: Person | Employee = {
  name: 'Jane',
  age: 20,
  employeeCode: '123'
};
console.log((person as Person).name);
```

With the `as` operator, we explicitly tell the TypeScript compiler that the `person` is of the `Person` class, so that we can access the `name` property which is in the `Person` interface.

* * *

Type Predicates
===============

To check for the structure of the object, we can use a type predicate. A type predicate is a piece code where we check if the given property name has a value associated with it.

For example, we can write a new function `isPerson` to check if an object has the properties in the `Person` type:

```
interface Person {
  name: string;
  age: number;
}
interface Employee {
  employeeCode: string;
}
let person: Person | Employee = {
  name: 'Jane',
  age: 20,
  employeeCode: '123'
};
const isPerson = (person: Person | Employee): person is Person => {
  return (person as Person).name !== undefined;  
}
if (isPerson(person)) {
  console.log(person.name);  
}
else {
  console.log(person.employeeCode);  
}
```

In the code above, the `isPerson` returns a `person is Person` type, which is our type predicate.

If we use that function as we do in the code above, then the TypeScript compiler will automatically narrow down the type if a union type is composed of two types.

In the `if (isPerson(person)){ ... }` block, we can access any member of the `Person` interface.

However, this doesn’t work if there are more than two types that form the union type. For example, if we have the following code:

```
interface Animal {
  kind: string;
}
interface Person {
  name: string;
  age: number;
}
interface Employee {
  employeeCode: string;
}
let person: Person | Employee | Animal = {
  name: 'Jane',
  age: 20,
  employeeCode: '123'
};
const isPerson = (person: Person | Employee | Animal): person is Person => {
  return (person as Person).name !== undefined;  
}
if (isPerson(person)) {
  console.log(person.name);  
}
else {
  console.log(person.employeeCode);  
}
```

Then the TypeScript compiler will refuse to compile the code and we’ll get the following error messages:

```
Property 'employeeCode' does not exist on type 'Animal | Employee'.Property 'employeeCode' does not exist on type 'Animal'.(2339)
```

This is because it doesn’t know the type of what’s inside the `else` clause since it can be either `Animal` or `Employee`. To solve this, we can add another `if` block to check for the `Employee` type as we do in the following code:

```
interface Animal {
  kind: string;
}
interface Person {
  name: string;
  age: number;
}
interface Employee {
  employeeCode: string;
}
let person: Person | Employee | Animal = {
  name: 'Jane',
  age: 20,
  employeeCode: '123'
};
const isPerson = (person: Person | Employee | Animal): person is Person => {
  return (person as Person).name !== undefined;  
}
const isEmployee = (person: Person | Employee | Animal): person is Employee => {
  return (person as Employee).employeeCode !== undefined;  
}
if (isPerson(person)) {
  console.log(person.name);  
}
else if (isEmployee(person)) {
  console.log(person.employeeCode);  
}
else {
  console.log(person.kind);  
}
```

* * *

In Operator
===========

Another way to check the structure to determine the data type is to use the `in` operator. It’s like the JavaScript `in` operator, where we can use it to check if a property exists in an object.

For example, to check if an object is a `Person` object, we can write the following code:

```
interface Animal {
  kind: string;
}
interface Person {
  name: string;
  age: number;
}
interface Employee {
  employeeCode: string;
}
let person: Person | Employee | Animal = {
  name: 'Jane',
  age: 20,
  employeeCode: '123'
};
const getIdentifier = (person: Person | Employee | Animal) => {
  if ('name' in person) {
    return person.name;
  }
  else if ('employeeCode' in person) {
    return person.employeeCode
  }
  return person.kind;
  
}
```


In the `getIdentifier` function, we used the `in` operator as we do in ordinary JavaScript code. If we check the name of a member that’s unique to a type, then the TypeScript compiler will infer the type of the `person` object in the `if` block as we have above.

Since `name` is a property that’s only in the `Person` interface, then the TypeScript compiler is smart enough to know that whatever inside is a `Person` object.

Likewise, since `employeeCode` is only a member of the `Employee` interface, then it knows that the `person` object inside is of type `Employee`.

If both types are eliminated, then the TypeScript compiler knows that it’s `Animal` since the other two types are eliminated by the `if` statements.

* * *

Typeof Type Guard
=================

For determining the type of objects that have union types composed of primitive types, we can use the `typeof` operator.

For example, if we have a variable that has the union type `number | string | boolean`, then we can write the following code to determine whether it’s a number, a string, or a boolean. For example, if we write:

```
const isNumber = (x: any): x is number =>{
    return typeof x === "number";
}
const isString = (x: any): x is string => {
    return typeof x === "string";
}
const doSomething = (x: number | string | boolean) => {
  if (isNumber(x)) {
    console.log(x.toFixed(0));
  }
  else if (isString(x)) {
    console.log(x.length);
  }
  else {
    console.log(x);
  }
}
doSomething(1);
```

Then we can call number methods as we have inside the first `if` block since we used the `isNumber` function to help the TypeScript compiler determine if `x` is a number.

Likewise, this also goes for the string check with the `isString` function in the second `if` block.

If a variable is neither a number nor a string then it’s determined to be a boolean since we have a union of the number, string, and boolean types.

The `typeof` type guard can be written in the following ways:

*   `typeof v === "typename"`
*   `typeof v !== "typename"`

Where `“typename”` can be be `"number"`, `"string"`, `"boolean"`, or `"symbol"`.

* * *

Instanceof Type Guard
=====================

The `instanceof` type guard can be used to determine the type of instance type.

It’s useful for determining which child type an object belongs to, given the child type that the parent type derives from. For example, we can use the `instanceof` type guard like in the following code:

```
interface Animal {
  kind: string;
}
class Dog implements Animal{
  breed: string;
  kind: string;
  constructor(kind: string, breed: string) {    
    this.kind = kind;
    this.breed = breed;
  }
}
class Cat implements Animal{
  age: number;
  kind: string;
  constructor(kind: string, age: number) {    
    this.kind = kind;
    this.age = age;
  }
}
const getRandomAnimal = () =>{
  return Math.random() < 0.5 ?
    new Cat('cat', 2) :
    new Dog('dog', 'Laborador');
}
let animal = getRandomAnimal();
if (animal instanceof Cat) {
  console.log(animal.age);
}
if (animal instanceof Dog) {
  console.log(animal.breed);    
}
```

In the code above, we have a `getRandomAnimal` function that returns either a `Cat` or a `Dog` object, so the return type of it is `Cat | Dog`. `Cat` and `Dog` both implement the `Animal` interface.

The `instanceof` type guard determines the type of the object by its constructor, since the `Cat` and `Dog` constructors have different signatures, it can determine the type by comparing the constructor signatures.

If both classes have the same signature, the `instanceof` type guard will also help determine the right type. Inside the `if (animal instanceof Cat) { ... }` block, we can access the `age` member of the `Cat` instance.

Likewise, inside the `if (animal instanceof Dog) {...}` block, we can access the members that are exclusive to the `Dog` instance.

* * *

Conclusion
==========

With various type guards and type predicates, the TypeScript compiler can narrow down the type with conditional statements.

Type predicate is denoted by the `is` keyword, like `pet is Cat` where `pet` is a variable and `Cat` is the type. We can also use the `typeof` type guard for checking primitive types, and the `instanceof` type guard for checking instance types.

Also, we have the `in` operator checking if a property exists in an object, which in turn determines the type of the object by the existence of the property.