TypeScript is a superset of JavaScript that offers several features to help build more robust and maintainable code. One of these features is readonly properties, which allow developers to create immutable objects. Immutable objects are objects whose state cannot be changed once they are created, which can be helpful in preventing bugs caused by accidental changes to object state.
In this post, we'll explore how to create immutable objects using readonly properties in TypeScript.
Readonly properties are properties that can only be assigned a value during object initialization. Once assigned, their value cannot be changed. This feature is useful when you want to create objects whose state should not be changed after creation.
To create a readonly property in TypeScript, you can use the readonly
keyword before the property name. Here's an example:
class Person {
readonly name: string;
readonly age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const person = new Person("John", 30);
person.name = "Jane"; // Error: Cannot assign to 'name' because it is a read-only property.
In this example, we define a Person
class with two readonly properties: name
and age
. The constructor initializes these properties, and once initialized, their values cannot be changed.
Using readonly properties has several benefits:
One of the main benefits of readonly properties is that they prevent accidental changes to object state. In the example above, if we didn't make the name
and age
properties readonly, it would be possible to change their values after object initialization, which could lead to unexpected behavior and bugs.
Readonly properties can also improve code readability by making it clear which properties should not be changed after object initialization. This can be helpful when working with complex objects or collaborating with other developers.
Readonly properties can also help enforce design decisions, such as ensuring that certain properties are only set during object initialization. This can be useful when working on large projects with multiple developers or when developing libraries or frameworks.
While readonly properties can be helpful in preventing accidental changes to object state, they have some limitations:
Readonly properties only provide shallow immutability, which means that if an object contains non-readonly properties, those properties can still be changed. For example:
class Person {
readonly name: string;
address: { street: string; city: string };
constructor(name: string, street: string, city: string) {
this.name = name;
this.address = { street, city };
}
}
const person = new Person("John", "123 Main St", "Anytown");
person.address.street = "456 Main St"; // No error
In this example, the Person
class has a readonly name
property and a non-readonly address
property. While the name
property cannot be changed after object initialization, the address
property can still be modified.
Readonly properties also do not seal objects, which means that new properties can still be added to an object after initialization. For example:
class Person {
readonly name: string;
readonly age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
Object.seal(this);
}
}
const person = new Person("John", 30);
person.address = "123 Main St"; // No error
In this example, we use Object.seal
to prevent new properties from being added to the Person
object after initialization. However, because readonly properties do not seal the object, we can still add new properties to the object.
Readonly properties in TypeScript provide a way to create immutable objects, which can help prevent bugs caused by accidental changes to object state. While readonly properties have some limitations, they can be helpful in improving code readability and enforcing design decisions.
If you're interested in learning more about TypeScript, check out the resources below: