Type Guards

A type guard is a boolean expression that does a runtime check to guarantee the type in a certain scope.

Built-in Type Guards

Type Guard with typeof

A typeof type guard can protect primitive types.

1
2
3
4
5
6
7
8
function add1000(input: string | number): number {
if (typeof input === 'number') {
// Type guard helped us to detect a "number" (saves us from using "parseInt")
return input + 1000;
} else {
return parseInt(input, 10) + 1000;
}
}

When dealing with complex types, a typeof type guard may not be very useful since the type of a complex type will always be "object" (see here). In such cases, an in type guard would be more effective.

Type Guard with in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type Dog = {
name: string;
bark: () => void;
run: () => void;
}

type Person = {
name: string;
shout: () => void;
walk: () => void;
}

function makeNoise(dogOrPerson: Dog | Person): void {
if ('bark' in dogOrPerson) {
// Type guard helped us to detect a "Dog"
dogOrPerson.bark();
} else {
dogOrPerson.shout();
}
}

While the in type guard is effective for checking plain objects, it is recommended to use the instanceof type guard when checking a class.

Type Guard with instanceof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Dog {
name?: string;
bark = () => {
};
run = () => {
};
}

class Person {
name?: string;
shout = () => {
};
walk = () => {
};
}

function makeNoise(dogOrPerson: Dog | Person): void {
if (dogOrPerson instanceof Dog) {
dogOrPerson.bark();
dogOrPerson.run();
} else {
dogOrPerson.shout();
dogOrPerson.walk();
}
}

Custom Type Guard

1
2
3
4
5
6
function isAxiosError(error: unknown): error is AxiosError {
if (error && typeof error === 'object' && 'isAxiosError' in error) {
return true;
}
return false;
}

Type Predicates

A type predicate is a syntax construct used to define the return type of a type guard. It allows the type guard to assert a specific type to its input. Here is an example of a type predicate: error is AxiosError