Skip to content

Fixing "Cannot assign to read only property" Error in JavaScript Arrays

When working with arrays in JavaScript/TypeScript, you may encounter the error TypeError: Cannot assign to read only property '0' of object '[object Array]'. This error occurs when attempting to modify an array that has been marked as read-only or frozen.

Problem Overview

The error typically happens in these scenarios:

  1. Immutable state management: When working with state management libraries like Redux or NgRx where state is immutable
  2. Input properties: When trying to mutate arrays received as @Input() properties in Angular
  3. Frozen objects: When working with objects that have been explicitly frozen using Object.freeze()

In the original problem, the issue occurred when trying to sort an array received as an @Input() in an Angular component after the array was refreshed from an API call.

Root Cause

JavaScript arrays are normally mutable, but when an array is:

  • Received as an input property in Angular
  • Part of an immutable state (Redux, NgRx, etc.)
  • Explicitly frozen with Object.freeze()

It becomes read-only, and any attempt to modify it directly will throw the error.

Solutions

1. Create a Copy Before Modification

The most straightforward solution is to create a copy of the array before attempting to modify it:

javascript
// Using spread operator (recommended)
const mutableArray = [...originalArray];

// Using slice()
const mutableArray = originalArray.slice();

// Using Array.from()
const mutableArray = Array.from(originalArray);

2. For Angular Input Properties

When working with Angular components, avoid mutating input arrays directly:

typescript
@Component({
  // Component configuration
})
export class MyComponent {
  private _taskList: any[] = [];
  
  @Input()
  set taskList(value: any[]) {
    // Create a copy when the input changes
    this._taskList = [...value];
  }
  
  get taskList(): any[] {
    return this._taskList;
  }
  
  exchange(a: number, b: number) {
    // Now we can safely mutate our internal copy
    const temp = this._taskList[a];
    this._taskList[a] = this._taskList[b];
    this._taskList[b] = temp;
  }
}

3. For Redux/State Management

When working with immutable state:

javascript
// Instead of this (will cause error):
const items = getItems(state);
items.sort((a, b) => a.order - b.order);

// Do this (create a copy first):
const items = [...getItems(state)];
items.sort((a, b) => a.order - b.order);

4. Complete Sorting Solution

Here's how to rewrite the sorting function to avoid the error:

typescript
async taskChange(value: string, taskOrder: string) {
  this.sortOrder = taskOrder;
  this.selectedValue = value;
  
  // Create a working copy of the array
  const workingArray = [...this.taskList];
  
  const expr = {
    asc: (a, b) => a > b,
    desc: (a, b) => a < b,
  };
  
  for (let i = 0; i < workingArray.length; i++) {
    for (let j = i + 1; j < workingArray.length; j++) {
      switch (value) {
        case 'export_name':
          if (expr[this.sortOrder](workingArray[i].name, workingArray[j].name)) {
            // Exchange elements in the working array
            const temp = workingArray[i];
            workingArray[i] = workingArray[j];
            workingArray[j] = temp;
          }
          break;
        case 'file_type':
          let type1 = this.exportType.transform(workingArray[i].code, []);
          let type2 = this.exportType.transform(workingArray[j].code, []);
          if (expr[this.sortOrder](type1, type2)) {
            const temp = workingArray[i];
            workingArray[i] = workingArray[j];
            workingArray[j] = temp;
          }
          break;
      }
    }
  }
  
  // Assign the sorted array back (if needed)
  this.taskList = workingArray;
}

WARNING

Note: The above implementation uses a bubble sort algorithm, which is inefficient for large arrays (O(n²) time complexity). For better performance, consider using JavaScript's built-in Array.prototype.sort() method.

5. Modern Approach with Built-in Sort

For better performance, use the native array sort method:

typescript
taskChange(value: string, taskOrder: string) {
  const workingArray = [...this.taskList];
  
  workingArray.sort((a, b) => {
    if (value === 'export_name') {
      const comparison = a.name.localeCompare(b.name);
      return taskOrder === 'asc' ? comparison : -comparison;
    } else if (value === 'file_type') {
      const typeA = this.exportType.transform(a.code, []);
      const typeB = this.exportType.transform(b.code, []);
      const comparison = typeA.localeCompare(typeB);
      return taskOrder === 'asc' ? comparison : -comparison;
    }
    return 0;
  });
  
  this.taskList = workingArray;
}

Preventative Measures

  1. Always assume inputs are immutable: Treat all input arrays as potentially read-only
  2. Use TypeScript: It can help catch some immutability issues at compile time
  3. Document immutable data: Clearly document which arrays should not be mutated
  4. Use immutable data structures: Consider libraries like Immutable.js for complex state management

Common Pitfalls

DANGER

These approaches will still cause the error:

javascript
// Direct assignment without copying
const arr = originalArray;

// Methods that modify arrays in place
originalArray.sort();
originalArray.reverse();
originalArray.push();
originalArray.pop();

Conclusion

The "Cannot assign to read only property" error occurs when attempting to mutate an array that has been marked as immutable. The solution is to always create a copy of the array before performing any mutations. Use the spread operator [...array], array.slice(), or Array.from(array) to create mutable copies of potentially immutable arrays.

This approach ensures your code works correctly with:

  • Angular input properties
  • Redux/state management systems
  • Any other scenario where arrays might be frozen or made read-only