🔷 TypeScript Q19 / 101

What is readonly modifier?

AI-Powered Answer ✓ Answered

The `readonly` modifier in TypeScript is a powerful keyword used to declare properties of a class or interface that can only be assigned a value during their declaration or within the constructor of the class. Once initialized, their value cannot be changed, providing a compile-time guarantee of immutability for that specific property.

What is `readonly`?

The readonly modifier allows you to mark properties of a class or interface as immutable after their initial assignment. This means that a readonly property can only be assigned a value at the point of its declaration or within the constructor of its containing class. After that, any subsequent attempts to modify it will result in a compile-time error from TypeScript.

It serves as a strong mechanism for enforcing immutability, ensuring that certain data remains constant throughout the lifecycle of an object instance. This enhances code predictability, reduces the likelihood of unintended side effects, and makes debugging easier by narrowing down where a property's value can originate from.

Key Characteristics

  • Initial Assignment: A readonly property must be initialized either at its declaration site or within the class's constructor.
  • Immutability: Once initialized, its value cannot be changed by any other method or direct assignment outside the constructor.
  • Compile-Time Enforcement: TypeScript enforces readonly at compile time, catching modification attempts early in the development cycle.
  • Applicability: It can be used with properties in interfaces, type aliases, and classes.

`readonly` in Interfaces and Type Aliases

When applied to properties within interfaces or type aliases, readonly ensures that any object conforming to that type cannot modify those specific properties once they have been initialized or assigned.

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

let p1: Point = { x: 10, y: 20 };

// Attempting to modify a readonly property results in a compile-time error:
// p1.x = 5; // Error: Cannot assign to 'x' because it is a read-only property.

`readonly` in Classes

In classes, readonly properties can be assigned a value either during their declaration or within the class's constructor. After the constructor has finished execution, the property becomes immutable, and any subsequent attempts to reassign it from inside or outside the class will be flagged as an error.

typescript
class Circle {
  readonly radius: number; // Declared, will be initialized in constructor
  readonly color: string = "blue"; // Initialized at declaration

  constructor(radius: number) {
    this.radius = radius; // Valid: Assignment in constructor
    // This.color = "red"; // Also valid: Assignment in constructor is allowed
  }

  scale(factor: number) {
    // this.radius = this.radius * factor; // Error: Cannot assign to 'radius' because it is a read-only property.
    console.log(`Cannot change radius for immutable circle. Current radius: ${this.radius}`);
  }
}

const myCircle = new Circle(10);
// myCircle.radius = 12; // Error: Cannot assign to 'radius' because it is a read-only property.

`readonly` vs `const`

It's a common point of confusion, but readonly and const serve different purposes and apply to different scopes:

  • const: Applies to *variables*. It ensures that the variable's *reference* cannot be reassigned. If the variable holds a primitive value, that value is immutable. If it holds an object or array, the *reference* to that object/array cannot change, but the *contents* of the object/array might still be mutable.
  • readonly: Applies to *properties* of an object (within interfaces, type aliases, or classes). It ensures that a specific property cannot be reassigned *after its initial assignment* (which happens during declaration or in the constructor).
typescript
// Example of `const`
const PI = 3.14159;
// PI = 3; // Error: Cannot assign to 'PI' because it is a constant.

const myArray = [1, 2, 3];
// myArray = [4, 5]; // Error: Cannot assign to 'myArray' because it is a constant.
myArray.push(4); // Valid: The content of the array can still be modified.
myArray[0] = 0; // Valid: The content can be modified.

// Example of `readonly` (see previous code blocks for class/interface examples)

When to use `readonly`

  • Immutable Configuration: For configuration objects or settings that should not change after an application or component has been initialized.
  • Data Transfer Objects (DTOs): To define DTOs where properties are set upon creation and then expected to remain fixed for their lifespan.
  • Value Objects: In domain-driven design, to clearly define value objects that are inherently immutable and identified by their attribute values.
  • State Management: To ensure certain parts of an object's internal state are fixed, preventing unintended modifications and making state transitions more predictable.
  • Public API Contracts: To clearly communicate to consumers of a class or interface that specific properties are not intended for modification, thereby enforcing a contract for immutability.