7

I am confused by the spread operator in typescript

When I use .the spread operator to make a copy of object1.

  var object2 =  { ...object1, };

I get a new object2 with a deep copy of all object1 items even if object1 contains other objects.

However if object1 has an array in it a shallow copy is performed. In that case it seems to maintain the relationship between the array values in object1 and object2.

Is there a way to deep copy arrays using the spread operator?

AJM
  • 32,054
  • 48
  • 155
  • 243

4 Answers4

5

new object2 with a deep copy of all object1 items

No. Spread is always a shallow copy.

Example

let orig = { arr: [1,2,3] }
let copy = {...orig}
copy.arr.push(4)
console.log(orig.arr) // 1 2 3 4

More

Some docs: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#object-spread-and-rest

user229044
  • 232,980
  • 40
  • 330
  • 338
basarat
  • 261,912
  • 58
  • 460
  • 511
  • But thise docs say "You can also spread an object into another object. A common use case is to simply add a property to an object without mutating the original:" So if we are not mutating the original is it not a deep copy? – AJM Oct 27 '17 at 08:17
  • 1
    It is not true, if you have object with field which is an array spread operator would copy reference to this array and if you change this array in any object created by spread they all changes. Chrome console: https://pastebin.com/JM5bgbvM – Adam Michalski Oct 27 '17 at 13:31
  • @AdamMichalski I added an example – basarat Oct 27 '17 at 19:20
3

if you need deep copy, how about:

var object2 = JSON.parse(JSON.stringify(object1));
capo11
  • 790
  • 3
  • 11
  • 32
Datum Geek
  • 1,358
  • 18
  • 23
1

Spread operator only copies the references to the elements of the original array. Array elements point to the same memory locations.

For deep copy I am using cloneDeep from lodash.

Adam Michalski
  • 1,722
  • 1
  • 17
  • 38
0

This does not answer the question directly, but I want to tell you how I do it. In typescript I usually make classes for my objects, and constructors for deep copying objects of the same type. You can then pass an object of the same type, or the attributes directly using two constructors.

export class MyObject {
  myAttribute:string;
  otherAttributes:Array<string>;

  constructor();
  constructor(myAttributeOrObj?:MyObject|string);
  constructor(myAttributeOrObj?:any, otherAttributes:Array<string>){
    if(myAttributeOrObj && myAttributeOrObj.myAttribute){
      this.myAttribute = myAttributeOrObj.myAttribute;
      this.createOtherAttributes(myAttributeOrObj.otherAttributes);
    }else{
      this.myAttribute=myAttributeOrObj;
      this.createOtherAttributes(otherAttributes);
    }
  }

  createOtherAttributes(arr){
      this.otherAttributes = [];
      for(var i = 0; i< arr.length; i++){
        this.otherAttributes.push(arr[i]);           
      }
  }

}

This method may create overhead compared to using existing libraries, but with this method you have full control of your objects, types and know you have made a deep copy instead of a shallow one.

More information about constructor overload can be found in this question Constructor overload in TypeScript

John
  • 10,165
  • 5
  • 55
  • 71