Readonly Properties in TypeScript


In TypeScript, properties are an essential part of defining the structure and behavior of objects. They allow us to encapsulate data and provide a way to access and manipulate it. By default, properties in TypeScript can be both read and write, meaning they can be both accessed and modified. However, there are scenarios where we may want to create properties that can only be read and not modified. This is where readonly properties come into play.

Readonly properties provide a way to define properties that can only be accessed and not changed once they are assigned a value. They are particularly useful in scenarios where you want to ensure that certain data remains constant and cannot be accidentally modified. In this tutorial, we will explore readonly properties in TypeScript, understand how they work, and see practical examples of their usage.

Syntax

To define readonly properties in TypeScript, we prefix the property name with the readonly keyword.

The encapsulating entity may be a class or an interface in TypeScript.

class MyClass {
   readonly propertyName: type;
}

You can replace MyClass with the name of your class, propertyName with the desired name for the readonly property, and type with the appropriate data type.

Understanding Readonly Properties

These readonly properties can only be read and never be modified. Any attempt to modify the readonly property will result in a compile-time error. Also, note that there are no run-time checks to enforce this in TypeScript, and the resulting JavaScript equivalent code doesn’t have any way to validate the readonly property.

In the below example, the name and age properties of the Person class are marked as readonly. Once these properties are assigned a value, their values cannot be changed, and any attempt will result in linting and compile time error.

class Person {
   readonly name: string;
   readonly age: number;

   constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
   }
}

Usage and Benefits of Readonly Properties

Readonly properties offer several benefits and use cases in TypeScript development. Let's explore some of them −

Example 1: Immutable Objects

Readonly properties can be used to create immutable objects. An immutable object is an object whose state cannot be changed after it is created. By marking all properties as readonly, we ensure that the object's state remains constant throughout its lifetime. This is especially useful in scenarios where you want to guarantee that certain data remains unchanged, preventing accidental modifications that could introduce bugs or unexpected behavior.

class Circle {
   readonly radius: number;
   readonly area: number;

   constructor(radius: number) {
      this.radius = radius;
      this.area = Math.PI * radius * radius;
   }
}

const circle = new Circle(5);
console.log(`The area of the circle is: ${circle.area}`); // Output: 78.53981633974483
// circle.area = 100; 

On compiling, it will generate the following JavaScript code −

var Circle = /** @class */ (function () {
   function Circle(radius) {
      this.radius = radius;
      this.area = Math.PI * radius * radius;
   }
   return Circle;
}());
var circle = new Circle(5);
console.log("The area of the circle is: ".concat(circle.area)); // Output: 78.53981633974483
// circle.area = 100;

Output

The above code will produce the following output −

The area of the circle is: 78.53981633974483

In the above example, the radius and area properties of the Circle class are marked as readonly. Once the Circle object is created, its properties cannot be modified, ensuring that the calculated area remains constant and consistent with the radius value.

The last line tries to modify the readonly property area of the circle object and hence will result in an error. You can check it by uncommenting the above line of code.

Error: Cannot assign to 'area' because it is a read-only property.

Example 2: Safe Refactoring

Readonly properties also play a significant role when refactoring code. When you mark a property as readonly, the TypeScript compiler helps you identify and prevent accidental modifications to that property. If you try to modify a readonly property, the compiler will raise an error, making it easier to catch and fix potential bugs during development.

class Car {
   readonly brand: string;
   readonly model: string;

   constructor(brand: string, model: string) {
      this.brand = brand;
      this.model = model;
   }
}

const car = new Car("Toyota", "Corolla");
console.log(`The brand of the car is: ${car.brand}`); // Output: "Toyota"
// car.brand = "Honda"; // Error: Cannot assign to 'brand' because it is a read-only property

On compiling, it will generate the following JavaScript code −

var Car = /** @class */ (function () {
   function Car(brand, model) {
      this.brand = brand;
      this.model = model;
   }
   return Car;
}());
var car = new Car("Toyota", "Corolla");
console.log("The brand of the car is: ".concat(car.brand)); // Output: "Toyota"
// car.brand = "Honda"; // Error: Cannot assign to 'brand' because it is a read-only property

Output

The above code will produce the following output −

The brand of the car is: Toyota

In the above example, the brand and model properties of the Car class are marked as readonly. If we attempt to assign a new value to the brand property, the TypeScript compiler will raise an error, preventing accidental modifications.

Example 3: Enhanced Readability and Intent

By using readonly properties, we can communicate the intended behavior and usage of certain properties more explicitly. When you encounter a readonly property in a class or interface, it becomes evident that its value is not meant to be modified. This improves code readability and makes it easier for other developers to understand the codebase and follow best practices.

interface Point {
   readonly x: number;
   readonly y: number;
}

function calculateDistance(point1: Point, point2: Point): number {
   const dx = point2.x - point1.x;
   const dy = point2.y - point1.y;
   return Math.sqrt(dx * dx + dy * dy);
}

const point1: Point = { x: 0, y: 0 };
const point2: Point = { x: 3, y: 4 };
// point1.x = 5; // Error: Cannot assign to 'x' because it is a read-only property.
console.log(
   `The distance between the points is: ${calculateDistance(point1, point2)}`
); // Output: 5

On compiling, it will generate the following JavaScript code −

function calculateDistance(point1, point2) {
   var dx = point2.x - point1.x;
   var dy = point2.y - point1.y;
   return Math.sqrt(dx * dx + dy * dy);
}
var point1 = { x: 0, y: 0 };
var point2 = { x: 3, y: 4 };
// point1.x = 5; // Error: Cannot assign to 'x' because it is a read-only property.
console.log("The distance between the points is: ".concat(calculateDistance(point1, point2))); // Output: 5

Output

The above code will produce the following output −

The distance between the points is: 5

In the above example, the x and y properties of the Point interface are marked as readonly. This clearly communicates that the coordinates of a point are not expected to change during the calculation of the distance.

Conclusion

Readonly properties in TypeScript allow us to define properties that can only be read and not modified. Using the readonly keyword modifier can enforce immutability and prevent accidental modifications to certain data. Readonly properties offer several benefits, including the creation of immutable objects, safer refactoring, and improved code readability. They play a crucial role in maintaining code integrity, catching potential bugs during development, and conveying the intended behavior of properties.

When designing TypeScript classes and interfaces, consider using readonly properties where appropriate to ensure data consistency and reduce the likelihood of introducing unexpected behavior through accidental modifications. By leveraging readonly properties effectively, you can create more robust and reliable TypeScript codebases.

Updated on: 31-Aug-2023

150 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements