Immutability is a powerful concept in programming that helps prevent unintended side effects in a codebase by ensuring that once a variable or object is created, it cannot be modified. In TypeScript, we can enforce immutability using a combination of ‘readonly‘, mapped types, and utility types such as ‘ReadonlyArray‘.
Let’s see some examples of how we can achieve immutability in TypeScript:
1. **Using ‘readonly‘**: You can use the ‘readonly‘ keyword with properties to enforce that they cannot be modified after being initialized.
class ImmutablePoint {
readonly x: number;
readonly y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
const point = new ImmutablePoint(3, 4);
point.x = 5; // Error: Cannot assign to 'x' because it is a read-only property
2. **Using mapped types**: Mapped types allow you to create a new type based on an existing type, with some transformations applied to its properties. To make all properties of a type read-only, we can use a mapped type:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Point {
x: number;
y: number;
}
const mutablePoint: Point = { x: 3, y: 4 };
mutablePoint.x = 5; // OK
const immutablePoint: Readonly<Point> = { x: 3, y: 4 };
immutablePoint.x = 5; // Error: Cannot assign to 'x' because it is a read-only property
Here, ‘Readonly<T>‘ is a mapped type that enforces all properties of ‘T‘ to be read-only using ‘readonly‘.
3. **Using utility types**: TypeScript provides some built-in utility types such as ‘ReadonlyArray‘ and ‘Readonly<T>‘ that can help enforce immutability:
- ‘ReadonlyArray<T>‘: Enforces immutability for arrays.
const mutableArray: number[] = [1, 2, 3];
mutableArray.push(4); // OK
const immutableArray: ReadonlyArray<number> = [1, 2, 3];
immutableArray.push(4); // Error: Property 'push' does not exist on type 'ReadonlyArray<number>'
- ‘Readonly<T>‘: This utility type is essentially the same as the mapped type we defined in the previous step.
const immutablePoint: Readonly<Point> = { x: 3, y: 4 };
immutablePoint.x = 5; // Error: Cannot assign to 'x' because it is a read-only property
In summary, TypeScript’s type system allows us to enforce immutability by using the ‘readonly‘ keyword, creating mapped types, and leveraging built-in utility types like ‘ReadonlyArray‘ and ‘Readonly<T>‘. These tools applied together help to ensure that developers adhere to the principles of immutability, leading to safer and more maintainable code.