Everything you need to know about Typescript Type Guards
Tags: typescript, javascript, tutorial
Published: 2023-02-12 11:00:00

Type guards can be used to narrow down the type of any value in TypeScript. They can be used to check the type of variables, function return values, or elements in arrays, for example.

In general, type guards can be used whenever you have a value that could be of multiple types and you need to determine its actual type in order to perform specific operations on it. The type guard allows you to tell the TypeScript compiler more information about the type of a value, allowing it to provide better type checking and autocompletion in your code.

There are several built-in type guards available in TypeScript:

1. `typeof` type guard: checks if a value is of a certain primitive type (e.g. `number`, `string`, `boolean`),
2. `in` type guard: checks if a value is a property of an object or one of the properties of an union type,
3. `instanceof` type guard: checks if a value is an instance of a particular class or interface,
4. `is` type guard: a custom type guard that can be created by checking the shape of an object,
5. `never` type guard: represents values that never occur.

## typeof
The `typeof` operator is used to check the type of a value in JavaScript. It returns a string representing the type of the operand.
```typescript
function add1000(input: string | number): number {
	if (typeof input === 'number') {
		return input + 1000;
	} else {
		return parseInt(input, 10) + 1000;
	}
}
```

In this example, `typeof input === ‘number’` checks if the type of input is `number`. If it is, the type of input within the if block will be narrowed to `number`, and the `input + 1000` expression is valid because both operands have the same type.

`typeof` type guards can also be used with string literals, union types, and type aliases.

It's crucial to remember that `typeof` type guards are only effective when used with string, number, boolean, and symbol types. For more complex data types, you need to rely on `in` or `instanceof` type guards. The typeof type guard can identify the following data types: `undefined` returns `"undefined"`, `null` returns `"object"`, `boolean` returns `"boolean"`, `number` returns `"number"`, `BigInt` returns `"bigint"`, `string` returns `"string"`, `symbol` returns `"symbol"`, functions (including classes) return `"function"`, and any other object returns `"object"`. However, for non-primitive data types, `typeof` type guard is less effective and `instanceof` or `in` type guards are preferred.

## in
It basically solves the problem with `typeof` and objects. Checks if a property or method is available on an object and narrows the type of the object based on the result. For example:

```typescript
interface Dog {
  move: () => void,
  bark: () => string,
}

interface Person {
  move: () => void,
  talk: () => string,
}

function makeNoise(dogOrPerson: Dog | Person): void {
  if ('bark' in dogOrPerson) {
    dogOrPerson.bark() // here only bark and move methods will be shown
  } else {
    dogOrPerson.talk() // here only methods specific for Person will be available
  }
}

```

## instanceof
The `instanceof` type guard is used to check if an object is an instance of a particular class or constructor function. The `instanceof `operator is used to check if an object is an instance of a given constructor function. If the object is an instance of the constructor function, the type of the object is narrowed to be the type of the constructor function.

```typescript
class Car {
  wheels: number;
  constructor(wheels: number) {
    this.wheels = wheels;
  }
}

class SportsCar extends Car {
  doors: number;
  constructor(wheels: number, doors: number) {
    super(wheels);
    this.doors = doors;
  }
}

let vehicle: Car | SportsCar = new SportsCar(4, 2);

if (vehicle instanceof SportsCar) {
  console.log(`The vehicle has ${vehicle.doors} doors.`);
}
```

If the condition vehicle `instanceof` `SportsCar` is `true`, then the type of the `vehicle` variable is narrowed down from `Car | SportsCar` to `SportsCar`. This means that the TypeScript type checker now knows that the vehicle variable is of type `SportsCar` and allows you to access its properties and methods, such as `doors`, without any type errors.

The `instanceof` operator works by checking the prototype chain of an object. It returns `true` if the constructor of the prototype of the object is equal to the constructor passed as the argument. In this case, it returns `true` if the constructor of the prototype of the vehicle object is equal to the `SportsCar` constructor.

By using the `instanceof` type guard, you can improve the type safety of your code and avoid runtime errors. For example, if you tried to access the `doors` property of the vehicle object without checking its type first, you would get a runtime error if the `vehicle` object is actually an instance of the `Car` class, as it does not have a `doors` property. However, with the type guard in place, the TypeScript compiler will catch this error and prevent it from happening.

Another simple example checking if a value is an instance of a constructor function using Javascript `Date`:

```typescript
const value = new Date();
if (value instanceof Date) {
  console.log(value.getFullYear());
}
```

## Type predicates - `is`
A type predicate is a special type of type guard that allows you to narrow down the type of a value based on some condition.

```typescript
import axios, { AxiosError } from 'axios';

const isAxiosError = (error: unknown): error is AxiosError => {
  if (error && typeof error === 'object') {
    return 'isAxiosError' in error;
  }
  return false;
};

const fetchData = async () => {
  try {
    await axios.get('http://examole.com/api');
  } catch (error: unknown) {
    if (isAxiosError(error)) {
      console.error(`Failed with HTTP status "${error.status}`);
    }
  }
};
```

Remember that you can throw any value, including a `string` or a `number`, so we want to make sure that  we use properties that are present in our `error` object.

In this case, the type predicate is checking if the value of `error` is of type `AxiosError` by using the `in` operator to check if the `isAxiosError` property is present in the error object.

The `is` type guard is a way to specify that the type of the value being checked is the type specified after `is`. For example, `error is AxiosError`. If the type predicate `isAxiosError` returns `true`, the type of `error` is narrowed down to `AxiosError` within the `if` block, allowing you to access the properties of an `AxiosError` object such as status.

Without this type predicate, you would have to either throw an error or cast the value to `AxiosError`, both of which can be error-prone. Throwing an error would add overhead and complexity to the code, and casting the value to `AxiosError` would ignore the possibility that the error is not of type `AxiosError`.

By using the `is` type guard, you can ensure that the code inside the `if` block only runs if the error is indeed an `AxiosError`, avoiding the possibility of an error being thrown or any other type being used.

## never
The `never` type in represents values that never occur. It is a bottom type, which means that it is a subtype of all other types.

A `never` type guard is a way to narrow down the type of an object to the `never` type. This is useful in cases where you want to express that a certain piece of code should never be reached, or that a certain function should never return a value.

A common use case of this type is checking all available cases with `switch` statement:

```typescript
enum ResponseType {
  Success = "Success",
  Failure = "Failure",
  Error = "Error",
}

function handleResponse(response: ResponseType): string {
  switch (response) {
    case ResponseType.Success:
      return "The operation was successful.";
    case ResponseType.Failure:
      return "The operation failed.";
    case ResponseType.Error:
      return "An error occurred."
    default:
      const neverValue: never = response;
      throw new Error(`Unexpected response type: ${neverValue}`);
  }
}
```

 The type of the variable `neverValue` is declared as `never`, which represents values that never occur. This is used to express that the default case should never be reached, as all possible values of the response parameter are covered by the `switch` statement.

By using the `never`  type in this way, you can enforce type safety and prevent bugs. If a new value is added to the `ResponseTypee` enum in the future, and the switch statement is not updated to handle it, the default case will be triggered, and an error will be thrown with a message indicating the unexpected response type. This can help catch bugs early and improve the overall quality of your code.

It’s worth noting that in this example, the `never` type is not used as a type guard. Instead, it is used to explicitly declare the type of a value that should never occur. The purpose of this is to indicate to the TypeScript compiler that the default case should never be reached, and to provide a more descriptive error message in case it is reached.

## Conclusion

There are several built-in type guards available in TypeScript, including `typeof`, `in`, `instanceof`, `is`, and `never`. The `typeof` type guard is used to check the type of a value and can only be used with primitive types. The `in` type guard is used to check if a property is available on an object and narrows the type of the object based on the result. The `instanceof` type guard is used to check if an object is an instance of a particular class or constructor function, and narrows the type of the object if it is. By using type guards, you can improve the type safety of their code and avoid runtime errors.

### ⚡️ Action Points
- [see my blog a about Javascript and React](https://www.nikolasbarwicki.com/)
- give a like or write a comment