Search code examples
typescriptreadonly

TypeScript: Why does readonly work so differently with arrays?


Here is a simple example of a class in which I declared two arrays in different ways:

  1. readonly arr: string[];
  2. arr2: ReadonlyArray<string>;
class ReadonlyArrsClass {
  readonly arr: string[] = ['1', '2'];
  arr2: ReadonlyArray<string> = ['1', '2'];

  addToArr(): void {
    this.arr.push('333'); // okay.
  }

  addToArr2(): void {
    this.arr2.push('444'); // error!
  }

  changeArrData(): void {
    this.arr = ['555']; // error!
  }
  changeArr2Data(): void {
    this.arr2 = ['666']; // okay.
  }

   logToConsole(): void {
    console.log(this.arr);
    console.log(this.arr2);
  }

  constructor(){
    this.addToArr();
    this.addToArr2();
    this.logToConsole();
    this.changeArrData();
    this.changeArr2Data();
    this.logToConsole();
  }
}

new ReadonlyArrsClass;

Why in case 1. I can still add elements to the end of the arr using push()?

But in case 2. I can’t. (which seems more logical). I'm getting the error Property 'push' does not exist on type 'readonly string[]'.


Why in case 2. I can reassign the reference of the arr2?

But in case 1. I get an error - Cannot assign to 'arr' because it is a read-only property.


Can someone explain why this works this way and what is preferable to use 🤷🏻?

Or don't use it at all 🙈


Solution

  • So, I understood the following: It is necessary to separate

    • readonly arr
    • string[]
    • ReadonlyArray<string>

    and then it will become clear what the difference is.

    When you use readonly arr, then we are talking about an object and if you initialize it, you cannot change it (including its type). That is, you cannot reassign a new array, but you can push into an old one.

    When you use string[] or ReadonlyArray<string>, then we are talking only about the type and only the type is responsible for the behavior - in this case, if arr does not have readonly, then you can create and assign new objects.

    In terms of use, you need to understand what is needed for the task:

    1. readonly arr: ReadonlyArray<string> - is essentially a constant that you defined and you cannot change in any way, neither assign a new object (change the reference), nor push it.
    2. arr: ReadonlyArray<string> - you can assign a new array, but you cannot push.
    3. readonly arr: string[] - you can push, but you cannot assign new array.

    Then, based on the task, you choose what to use:

    • If you define a collection that needs to be supplemented, choose option 3.
    • If you want to reassign - option 2.
    • If neither this nor that - option 1.