Assertions in TypeScript
Tomohiro Yoshida
Posted on April 23, 2023
In TypeScript, there is a technique that is so-called Assertions.
Generally, it is better to avoid using assertion because it forces the TypeScript type system to write its type over. However, there are some situations it is reasonable to use assertion to make your code work as you expect. In this article, I will share assertions and how to use assertions in your project.
Type Assertions
When you use certain methods, it may return something that you did not expect. Here is an example.
const originalArray = ["hello", "world"];
// const originalArray: string[]
const jsonString = JSON.stringify(originalArray);
// const jsonString: string
const parsedArray = JSON.parse(jsonString);
// const parsedArray: any
console.log(parsedArray);
// [LOG]: ["hello", "world"]
In this example, the type of parsedArray
becomes any
though we can retrieve the data we want. This is a good use case of type assertion because the JSON.parse
method always returns any
.
(method) JSON.parse(text: string, reviver?: ((this: any, key: string, value: any) => any) | undefined): any
That is why, we will have to use assertion to notify the TypeScript type system of the actual type of the returned value. Here is an updated code.
const originalArray = ["hello", "world"];
// const originalArray: string[]
const jsonString = JSON.stringify(originalArray);
// const jsonString: string
const parsedArray = JSON.parse(jsonString) as string[];
// const parsedArray: any
console.log(parsedArray);
// [LOG]: ["hello", "world"]
Perfect! You can avoid unexpected runtime errors by using assertions properly
Let me give you another example.
const element = document.getElementById('myElement');
console.log(element.innerText);
// type error: 'element' is possibly 'null'.
In this above example, you will encounter the type error, "'element' is possibly 'null'.". That is because document.getElementById
returns union type, HTMLElement | null
. Therefore, it will be good to cast the HTMLElement
type if you are 100% sure that it is not null
.
const element = document.getElementById('myElement') as HTMLElement;
console.log(element.innerText); // OK
Please note that type assertion is not the only solution in this scenario. You may be able to use type guard instead like below:
const element = document.getElementById('myElement');
if (element) {
console.log(element.innerText); // OK
}
Non-Null Assertions
Speaking of the document.getElementById
method, you may be able to use Non-null assertion if it is enough to tell the TypeScript type checker that the type of the returned value is NOT null.
By adding a !
instead of as type
, you can just notify the type checker that the returned value will never be null
. Here is an example:
const element = document.getElementById('myElement')!;
console.log(element.innerText); // OK
Const Assertions
Finally, let me introduce const assertions that work differently from other assertions.
When you use the const assertions, values affected by the const assertion will freeze.
For example, if a value is Array, it will be a readonly tuple that is an immutable array. If a value is Number or String, the type of the value becomes literal
instead of the general primitive. If you use it for an object, the properties of the object will be readonly.
Here is an example:
// Readonly tuple (immutable array)
const coordinates = [1, 2, 3] as const;
// coordinates: readonly [1, 2, 3]
// The following line will cause a TypeScript error because
// the array is now a readonly tuple and cannot be modified.
// coordinates[0] = 5;
// Literal types
const age = 30 as const;
// age: 30
// The following line will cause a TypeScript error because
// age is now a literal type and cannot be assigned a new value.
// age = 31;
const student = "John" as const;
// student: "John"
// The following line will cause a TypeScript error because
// him is now a literal type and cannot be assigned a new value.
// student = "Jane";
// Readonly properties for objects
const person = {
firstName: "Alice",
lastName: "Smith",
} as const;
// person: { readonly firstName: "Alice"; readonly lastName: "Smith" }
// The following line will cause a TypeScript error because
// person.firstName is now a readonly property and cannot be modified.
// person.firstName = "Bob";
In this example, I have used const assertions to create readonly tuples, literal types, and readonly properties for objects. Notice how trying to modify these values causes TypeScript errors, since const assertions make them immutable.
Conclusion
In conclusion, TypeScript assertions are a powerful tool for handling specific cases where type inference falls short or additional type information is required. They include type assertions, non-null assertions, and const assertions. While they can help prevent runtime errors and improve code readability, they must be used carefully. Using too many assertions can lead to potential runtime issues if used incorrectly. Always prioritize TypeScript's type inference and type-checking mechanisms, resorting to assertions only when necessary to ensure a robust and maintainable codebase.
Posted on April 23, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 31, 2024