Rezzy
Rezzy

Reputation: 141

Changing a specific property inside an object changes that same property in another object of the same type Typescript

I have a really weird problem, let me just explain:

Obj1: DetailsType = {property1: 123, property2: [{subProp1: 'a', subProp2: 'b'}]}
Obj2: DetailsType = new DetailsType(Obj1)

This is the constructor of DetailsType:

constructor(value: DetailsType = <DetailsType>{}){
    this.property1 = (value.property1 !== undefined) ? value.property1 : null
    this.property2 = (value.property2 !== undefined) ? value.property2 : []
}

Now I run the following code

this.Obj2.property1 = 987
this.Obj2.property2[0].subProp1 = 'z'

At this point, for some reason, the value of Obj1.property2[0].subProp1 is 'z' Even though we changed the value of subProp1 for Obj2! However, Obj1.property1 is still 123

So why does changing property2 which is an array, affect the value on both objects?? How can property1, a number, work correctly, but property2 work so weirdly? It works vice versa, whether I change subProp1 for Obj1 or Obj2. I'm so confused.

Thanks for your help!

Upvotes: 0

Views: 131

Answers (2)

Kevin Glier
Kevin Glier

Reputation: 1386

The current answer (with JSON.stringify) is maybe not the worst, because it will work in this case. But we don't know, how your objects in the array of prop2 will actually look like. Because it is a TypeScript class, it could be, that you also add some methods to it. With JSON.stringify though, all methods/functions would be lost. Only the value-properties are retained, but also not in the right order. The result is just some anonymous object.

Because it is sometimes necessary to clone objects (and not have their properties referenced to the original object), it is pretty useful to add a .clone() function that can be used with every object and array.

This will work by just adding the following code:

(<any>Object.prototype).clone = function() {
  const clone = (this instanceof Array) ? [] : {};
  for (const propertyName in this) {
    if (propertyName == 'clone') continue;
    if (this[propertyName] && typeof this[propertyName] == "object") {
      clone[propertyName] = this[propertyName].clone();
    } else clone[propertyName] = this[propertyName]
  } return clone;
};

With your given code, it will look like this (pretty clean):

class DetailsType {
  public prop1;
  public prop2;
  constructor(value) {
    this.prop1 = value.prop1 !== undefined ? value.prop1 : null;
    this.prop2 = value.prop2 !== undefined ? value.prop2.clone() : [];
  }
}

The .clone() method works both for arrays and objects and will create a deep copy.

Upvotes: 1

the Hutt
the Hutt

Reputation: 18408

It is happening because the value.property2 is an object with many nested references to other objects inside it. You need to deep clone the value.property2 in the constructor:

class DetailsType {
  constructor(value) {
    this.prop1 = (value.prop1 !== undefined) ? value.prop1 : null
    this.prop2 = (value.prop2 !== undefined) ? JSON.parse(JSON.stringify(value.prop2)) : []
  }
}

let obj1 = {
  prop1: 123,
  prop2: [{
    subProp1: 'a',
    subProp2: 'b'
  }]
};
let obj2 = DetailsType = new DetailsType(obj1);

obj2.prop1 = 987
obj2.prop2[0].subProp1 = 'z'

document.body.innerHTML += `obj1:<pre>${JSON.stringify(obj1, undefined, 2)}</pre>`;
document.body.innerHTML += `obj2:<pre>${JSON.stringify(obj2, undefined, 2)}</pre>`;

To find out various ways of deep cloning objects refer:

Upvotes: 2

Related Questions