Type Assertion Typescript

Dec 5, 2022 - 5 min read

Type Assertion Typescript

The Problem

Type assertion is a feature of typescript that allows you to override the type of a variable. It is a feature covered in books and tutorials but needs to be mastered or given good examples to shine genuinely. In this article, we will look at some of the use cases for type assertion in typescript and react.

The Basics

TypeScript is a popular programming language that is a typed superset of JavaScript. One of the powerful features of TypeScript is its type system, which allows developers to catch type-related bugs at compile time. It can help prevent runtime errors and improve the overall reliability of your code.

One way to work with the TypeScript type system is through type assertion functions. These functions allow you to override the type of a variable and tell the TypeScript compiler that a variable has a specific type. It can be helpful when you want to use a variable in a way that is different from its inferred type.

For example, suppose you have the following code:

let foo = 'Hello, world!';

The type of foo is inferred to be a string by the TypeScript compiler. But what if you want to treat foo as a different type, such as a number? You can use a type assertion function to do that:

let foo = 'Hello, world!'; let bar = foo as number;

In the above code, we use the as keyword to tell the TypeScript compiler that we want to treat foo as a number. The compiler will allow us to use bar as a number in our code.

Type assertion functions do not change the type of a variable; they tell the compiler that the variable has a specific type. If you try to use the variable in a way that is not consistent with the asserted type, you will still get a compile-time error.

If you would like to learn more about essential TypeScript Type Assertion Functions, read the official docs

Type Assertion Functions Usecases

Now with the basics out of the way, let's define the actual use case. You can assert type assertion functions to throw an error if the type does not match. The essential usefulness is that the code will only go through if the assumption is correct.

Let's use an example of a small independent vehicle dealership store that has a TS developer working tirelessly to build the best UX experience. (⌐⊙_⊙)

export type Car = { type: 'car'; title: string; description: string; }; export type Bike = { type: 'bike'; title: string; }; export type Tezzla = { type: 'tezzla'; plug: boolean; }; export type VehicleInStore = Car | Bike | Tezzla; export function isCar(item: VehicleInStore): item is Car { return item.type === 'car'; } export function isBike(item: VehicleInStore): item is Bike { return item.type === 'bike'; } export function isTezzla(item: VehicleInStore): item is Tezzla { return item.type === 'tezzla'; } export function assertCar(item: VehicleInStore): asserts item is Car { if (!isCar(item)) { throw new Error('Item is not a car, try Tezzla'); } } export function assertBike(item: VehicleInStore): asserts item is Bike { if (!isBike(item)) { throw new Error('Item is not a bike'); } } export function assertTezzla(item: VehicleInStore): asserts item is Tezzla { if (!isTezzla(item)) { throw new Error('Item is not a tezzla, try petrol'); } }
type Props = { item: VehicleInStore; }; export default function StoreComponent({ item }) { switch (item.type) { case 'car': assertCar(item); return <CarComponent car={item as Car} />; case 'bike': assertBike(item); return <BikeComponent bike={item as Bike} />; case 'electric': assertTezzla(item); return <TezzlaComponent tezzla={item as Tezzla} />; } return null; };

Exhaustive Bonus

We can expand on this example even further by checking that every case is provided. Sometimes it is easy to forget to add a case to a switch statement when new types of product appear. 🍄 This can lead to bugs in your code. To prevent this, you can use the never type to tell the TypeScript compiler that a switch statement is exhaustive.

The aim is to make sure that the compiler will throw an error if the default case is called. This will ensure that you have handled every possible case in your switch statement.

export type Car = { type: 'car'; title: string; description: string; }; export type Bike = { type: 'bike'; title: string; }; export type Tezzla = { type: 'tezzla'; plug: boolean; }; export type VehicleInStore = Car | Bike | Tezzla; export function isCar(item: VehicleInStore): item is Car { return item.type === 'car'; } export function isBike(item: VehicleInStore): item is Bike { return item.type === 'bike'; } export function isTezzla(item: VehicleInStore): item is Tezzla { return item.type === 'tezzla'; } export function assertCar(item: VehicleInStore): asserts item is Car { if (!isCar(item)) { throw new Error('Item is not a car, try Tezzla'); } } export function assertBike(item: VehicleInStore): asserts item is Bike { if (!isBike(item)) { throw new Error('Item is not a bike'); } } export function assertTezzla(item: VehicleInStore): asserts item is Tezzla { if (!isTezzla(item)) { throw new Error('Item is not a tezzla, try petrol'); } }
export function assertNever(value: never): never {
throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value, null, 2)}`);
}
type Props = { item: VehicleInStore; }; export default function StoreComponent({ item }) { switch (item.type) { case 'car': assertCar(item); return <CarComponent car={item as Car} />; case 'bike': assertBike(item); return <BikeComponent bike={item as Bike} />; case 'tezzla': assertTezzla(item); return <TezzlaComponent tezzla={item as Tezzla} />; default: assertNever(item); } return null; };

In summary, type assertion functions are a powerful tool for working with the TypeScript type system. They can help you catch type-related bugs at compile time and make your code more type-safe. By using type assertion functions, you can improve the reliability and maintainability of your TypeScript code. That's it, folks (҂◡_◡) ᕤ

What is the number one lesson you have learned from this article?