... and in this article, you'll learn why and how const, as const and readonly are applicable in practice. We won't just scratch the surface of the topic, but delve into the properties and differences of these keywords right at the heart of TypeScript: the compiler and the type system - let's go!
What role does the TypeScript compiler play?
To fully understand what const, as const and readonly do, we need to understand how the TypeScript compiler works. Broadly speaking, tsc performs 2 steps:
- Type checking
- Transpiling TypeScript to JavaScript
There is a stark contrast here to other common programming languages like Java, C#, or C++: Strong typing exists in TypeScript only until compile time. At runtime, JavaScript is used. The compiler tries to preserve as many JavaScript features as possible. This means that const (which is a JavaScript keyword) also applies at runtime (provided the target version of JavaScript supports const), but as const and readonly as pure TypeScript keywords only take effect during compile time.
Let's take a look at the following TypeScript code:
const colors = ['red', 'green', 'blue']
let colors2 = ['red', 'green', 'blue'] as const
const colors3 = ['red', 'green', 'blue'] as const This is transpiled (with Target: ES2021) into the following JavaScript code:
const colors = ['red', 'green', 'blue']
let colors2 = ['red', 'green', 'blue']
const colors3 = ['red', 'green', 'blue'] We see that as const has pure type-checking functionality and does not affect the produced target code. The same applies to readonly.
Typing in TypeScript
Typing works significantly differently in TypeScript compared to Java or C#. If no explicit type is specified, the compiler tries to determine which type is best suited for a variable (or constant). A variable assigned a string is typed as String:
let str1 = 'Foo' // Type: string A constant, on the other hand, is typed as a type with only one value:
const str2 = 'Bar' // Type: "Bar" This makes sense, as the constant can only take the value "Bar".
What types do the three arrays have?
Now the important question: What about the arrays?
const colors = ['red', 'green', 'blue'] // Type: string[]
let colors2 = ['red', 'green', 'blue'] as const // Type: readonly["red", "green", "blue"]
const colors3 = ['red', 'green', 'blue'] as const // Type: readonly["red", "green", "blue"] For arrays, the const keyword alone does not affect the type, but only whether it is a variable or a constant:
- const/let only determines whether we can assign a new value to color/color2 (e.g., a completely new array)
- as const determines whether the assigned array is readonly (i.e., immutable) or not. The generated type exactly matches the specific array with the 3 color strings as content
The effects of "const" and "as const"
The most important point first: A "const" array (or object) is mutable! To make it readonly, as we are used to from other programming languages, we need to use as const. Here are the details:
const
prevents another expression from being assigned to the constant. The content itself (which is located in memory elsewhere that the constant merely references) can be changed , provided it is an array or an object:as const
const colors = ['red', 'green', 'blue']
colors.push('yellow') // Ok -> Unintuitiver weise können wir das Array verändern, obwohl es "const" ist
colors = ['magenta', 'fuxia'] // Error: Cannot assign to 'colors' because it is a constant. defines a (readonly) type that exactly matches the content of the array: The second error message here is interesting: It indicates a type mismatch. Since colors2 is not a constant but a variable, an assignment is generally possible. However, only if the assigned type is also readonly["red", "green", "blue"]. colors3 has exactly this type, so this assignment could be made, but not the other way around (since colors3 is a constant):
let colors2 = ['red', 'green', 'blue'] as const
colors2.push('yellow') // Error: Property 'push' does not exist on type 'readonly ["red", "green", "blue"]'.
colors2 = ['magenta', 'fuxia'] // Error: Type '"magenta"' is not assignable to type '"red"'. Const objects are also mutable
let colors2 = ['red', 'green', 'blue'] as const
const colors3 = ['red', 'green', 'blue'] as const
colors2 = colors3 // Ok
colors3 = colors2 // Error: Cannot assign to 'colors3' because it is a constant. What applies to arrays also applies to objects. The
const keyword prevents a new value from being assigned to a constant, but the content of the constant can be changed:To truly make the object constant, we need to
const car = {
color: 'green',
seats: 5
}
car.seats = 4 // Unintuitiv: Hier können wir das Feld "seats" ändern, obwohl myCar const ist
console.log(car.seats) // 4 use as const: Practical tip: readonly So far, we haven't used the readonly keyword. This comes into play when defining functions, where we can use it in the type specification of parameters:
const car = {
color: 'green',
seats: 5
} as const
car.seats = 4 // Cannot assign to 'seats' because it is a read-only property. Another use of
readonly
function foo(colors: readonly string[]) {
colors.push('olive') // Error: Property 'push' does not exist on type 'readonly string[]'.
} is in classes. Here we can create readonly fields, which (unlike constants) can be set once in the constructor and then must not be changed: Conclusion Defensive programming helps to avoid or detect errors early. It is important to know the possibilities for this:
class Animal {
readonly name: string
constructor(name: string) {
this.name = name
}
public setName(name: string) {
this.name = name // Error: Cannot assign to 'name' because it is a read-only property.
}
} const
prevents direct assignments, but the content of the constant can be changed if that content has mutability, which is the case for arrays and objects
- as const makes the content of a variable constant (readonly) and prevents arrays and objects from having their content changed
- readonly is a keyword to mark function parameters and fields in classes as non-mutable
- Do you love TypeScript too? Then get in touch with us :) More TypeScript stuff
Speaking of defensive programming: On our YouTube channel, you can learn how to filter arrays in TypeScript type-safely:
Video: In-depth Tutorial on Type-Safe Array Filtering
TypeScript: const vs readonly - fewer errors through defe...