Last updated: Feb 26, 2024
Reading timeยท3 min
When using as const
in TypeScript, we can set the properties of an object or
the elements of an array to readonly
, indicating to the language that the type
in the expression will not be widened (e.g. from 42
to number
).
function sum(a: number, b: number) { return a + b; } // ๐๏ธ const arr: readonly [3, 4] const arr = [3, 4] as const; console.log(sum(...arr)); // ๐๏ธ 7
We created a sum
function that takes 2
numbers as parameters and returns the
sum.
2
elements of type number
.This is when you would use a const assertion in TypeScript.
The const assertion enables us to tell TypeScript that the array's type will not
be widened, e.g. from [3, 4]
to number[]
.
The array in the example becomes a readonly
tuple, so its contents cannot be
changed and we can safely unpack the two numbers in a call to the sum
function.
If you try to change the contents of the array, you will get an error.
function sum(a: number, b: number) { return a + b; } // ๐๏ธ const arr: readonly [3, 4] const arr = [3, 4] as const; // โ๏ธ ERROR: Property 'push' does not exist on type 'readonly [3, 4]' arr.push(5);
We used a const assertion, so the array is now a readonly
tuple whose contents
cannot be changed and attempting to do so causes an error during development.
If we try to call the sum
function without using a const
assertion, we would
get an error.
function sum(a: number, b: number) { return a + b; } // ๐๏ธ const arr: readonly [3, 4] const arr = [3, 4]; // โ๏ธ Error: A spread argument must either have a tuple // type or be passed to a rest parameter. console.log(sum(...arr)); // ๐๏ธ 7
TypeScript warns us that there is no way of knowing that the arr
variable
hasn't changed between its declaration and the time the sum()
function was
called.
readonly
tuple, so TypeScript can be sure that its contents will not change between its declaration and the function invocation.You can also use const assertions as a replacement for enums if you don't like using TypeScript's enum construct.
// ๐๏ธ const Pages: {readonly home: '/'; readonly about: '/about'...} export const Pages = { home: '/', about: '/about', contacts: '/contacts', } as const;
If you try to change any of the properties of the object or add a new property, you would get an error.
// ๐๏ธ const Pages: {readonly home: '/'; readonly about: '/about'...} export const Pages = { home: '/', about: '/about', contacts: '/contacts', } as const; // โ๏ธ Error: Cannot assign to 'about', because it is // a read-only property Pages.about = 'hello'; // โ๏ธ Error: Property 'test' does not exist on type ... Pages.test = 'hello';
It should be noted, however, that const
contexts do not convert an expression
to be fully immutable.
const arr = ['/about', '/contacts']; // ๐๏ธ const Pages: {readonly home: '/', menu: string[]} export const Pages = { home: '/', menu: arr, } as const; // โ Works Pages.menu.push('/test');
The menu
property references an external array, whose contents we are able to
change.
If the array was defined in place on the object, we wouldn't be able to change its contents.
// ๐๏ธ const Pages: {readonly home: '/', readonly menu: string[]} export const Pages = { home: '/', menu: ['/about'], } as const; // โ๏ธ Error: Property 'push' does not exist on type // 'readonly ["/about"]' Pages.menu.push('/test');
I've also written an article on how to change a readonly property to mutable.
You can learn more about the related topics by checking out the following tutorials: