Why type assertion is bad in Typescript?

Typescript

Posted by Theo Cha on September 29, 2022

Overview

One of TypeScript’s primary design principles is that “Statically identify constructs that are likely to be errors.”

While TypeScript provides type safety, it also gives us the ability to override the compiler’s type checker by using a language feature called type assertion.

Type assertions

Type assertion in TypeScript is the syntax and angle-bracket syntax made available by TypeScript to ‘assert’ any TypeScript identifier to a type of the implementer’s choosing.

1
2
3
4
5
6
7
// One is the as-syntax:
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;

// The other version is the “angle-bracket” syntax:
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;

Why is it bad?

While type assertions can be very useful when used in the right use case, they can be misused and weaken type safety because it doesn’t do any special data checking. Type assertions are a way to tell the compiler, “Just follow me, I know what I’m doing.” sometimes this can lead to the weakening of type safety which is the main purpose of using TypeScript.

Examples

It weakens Type Safety Example of type assertion using either of the two syntaxes :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export interface Human {
  name: string;
  age: number;
  occupation: string;
}
//here we are casting the object literal to Human using the `as` syntax for type assertion
const theo = {
  name: "Theo",
  age: 33,
} as Human;
//here we are casting the object literal to Human using the `ang-bracket` syntax of type assertion
const chelsea = <Human>{
  name: "Chelsea",
  age: 30,
};

Exceptional use cases

Here is an example of a valid use case of the type assertion

1
2
3
4
const deserialize = <T>(data: string): T => JSON.parse(data) as T;
const jake = deserialize<Person>(
  '{"name":"Jake", "age":24, "occupation": "artist"}'
);

This utility method lets you deserialize a JSON string to a known Type. This can be useful when deserializing WebSocket payloads for instance.

Here we are asserting that event is of type MouseEvent which seems to be a valid use case:

1
2
3
element.addEventListener("click", (event) => {
  let mouseEvent = event as MouseEvent;
});

Use Type Annotations Instead

Here is the best practice. we should use Type Annotations over Type Assertions. This empowers type safety and will cause compile-time errors if the declared types do not have any properties or are wrongly typed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// bad :(
const theo = {
  name: 'Theo',
  age: 33,
} as Human
// bad :(
const chelsea = <Human>{
  name: 'Chelsea',
  age: 30,
};
 
 
we should use the below version Instead,
// Good :)
const theo: Human = {
  name: 'Theo',
  age: 33,
  occupation: 'programmer'
};

This way if the object literal is missing any properties it will throw a runtime error because the typing is wrong. Sometimes we can’t avoid using type assertions. In these cases, it is considered better to use the as syntax over the<> angle-brackets syntax because the as syntax is recommended for JSX.

References