Cannot assign to read only property 'name' of object '[object Object]'

Viewed 105064

The following code will throw an error only for the name property. It could be fixed by specifying name property as writable in Object.create arguments but I'm trying to understand why is this happening(and maybe there is a more elegant way to fix it).

var BaseClass = function (data) {
  Object.assign(this, data);
}

var ExtendedClass = function () {
  BaseClass.apply(this, arguments);
}

ExtendedClass.prototype = Object.create(BaseClass);

console.log(new ExtendedClass({ type: 'foo' }));
new ExtendedClass({ name: 'foo' });

7 Answers

If you get this error in Angular+Typescript+NgRX:

You can use the spread operator to take a shallow copy of a readonly object to make it readable, however you may not want this depending on your situation.

let x = [...y];

If you're using Redux / NgRX, there's a chance your selector could be returning a readonly object with a reference to the store, which can throw exceptions when trying to alter that object property via template binding. Depending on your situation, you can take a deep copy to remove the store reference.

let x = JSON.parse(JSON.stringify(y));

If you get this error in Angular+TypeScript:

WRONG / INVALID:

@Output whatever_var = new EventEmitter();

GOOD / CORRECT:

@Output() whatever_var = new EventEmitter();

Used ES7+ or TypeScript spread operator feature to overcome this

obj = { ...obj, name: { first: 'hey', last: 'there'} }

I ran into this issue in Angular, while setting a local variable from ActivatedRoute's queryParams, and attempting to conditionally either override or merge... Duplicating beforehand did the trick:

updateQp(qp = {}, force = false) { 
    let qpLoc = Object.assign({}, this.queryParamsLocal)
    this.queryParamsLocal = force ? qp : Object.assign(qpLoc, qp)
}

In my case, I was trying to swop 2 elements in an array in redux. Here's a simple way of how I altered the redux state while avoiding the readonly issue:

let newData = []
let data = store.getState().events.value

for(let i = 0; i < data.length; i++) {
  if(i === fromIndex) {
    newData.push(data[toIndex])
  }else if(i === toIndex) {
    newData.push(data[fromIndex])
  }else {
    newData.push(data[i])
  }
}
this.setEventsData(newData)

The above code creates a new empty array. It then iterates through the readonly array and pushes each item into the new array. This effectively creates a new array with the items of the readonly array positioned in a different order. If you are having trouble editing the values, you could alter the above code and create a new object with the altered values and insert that into the position where you'd like to make the change.

Related