typescript - cloning object typescript - cloning object typescript typescript

typescript - cloning object


Solving The Specific Issue

You can use a type assertion to tell the compiler that you know better:

public clone(): any {    var cloneObj = new (this.constructor() as any);    for (var attribut in this) {        if (typeof this[attribut] === "object") {            cloneObj[attribut] = this[attribut].clone();        } else {            cloneObj[attribut] = this[attribut];        }    }    return cloneObj;}

Cloning

Bear in mind that sometimes it is better to write your own mapping - rather than being totally dynamic. However, there are a few "cloning" tricks you can use that give you difference effects.

I will use the following code for all the subsequent examples:

class Example {  constructor(public type: string) {  }}class Customer {  constructor(public name: string, public example: Example) {  }  greet() {    return 'Hello ' + this.name;  }}var customer = new Customer('David', new Example('DavidType'));

Option 1: Spread

Properties: Yes
Methods: No
Deep Copy: No

var clone = { ...customer };alert(clone.name + ' ' + clone.example.type); // David DavidType//alert(clone.greet()); // Not OKclone.name = 'Steve';clone.example.type = 'SteveType';alert(customer.name + ' ' + customer.example.type); // David SteveType

Option 2: Object.assign

Properties: Yes
Methods: No
Deep Copy: No

var clone = Object.assign({}, customer);alert(clone.name + ' ' + clone.example.type); // David DavidTypealert(clone.greet()); // Not OK, although compiler won't spot itclone.name = 'Steve';clone.example.type = 'SteveType';alert(customer.name + ' ' + customer.example.type); // David SteveType

Option 3: Object.create

Properties: Inherited
Methods: Inherited
Deep Copy: Shallow Inherited (deep changes affect both original and clone)

var clone = Object.create(customer);alert(clone.name + ' ' + clone.example.type); // David DavidTypealert(clone.greet()); // OKcustomer.name = 'Misha';customer.example = new Example("MishaType");// clone sees changes to original alert(clone.name + ' ' + clone.example.type); // Misha MishaTypeclone.name = 'Steve';clone.example.type = 'SteveType';// original sees changes to clonealert(customer.name + ' ' + customer.example.type); // Misha SteveType

Option 4: Deep Copy Function

Properties: Yes
Methods: No
Deep Copy: Yes

function deepCopy(obj) {    var copy;    // Handle the 3 simple types, and null or undefined    if (null == obj || "object" != typeof obj) return obj;    // Handle Date    if (obj instanceof Date) {        copy = new Date();        copy.setTime(obj.getTime());        return copy;    }    // Handle Array    if (obj instanceof Array) {        copy = [];        for (var i = 0, len = obj.length; i < len; i++) {            copy[i] = deepCopy(obj[i]);        }        return copy;    }    // Handle Object    if (obj instanceof Object) {        copy = {};        for (var attr in obj) {            if (obj.hasOwnProperty(attr)) copy[attr] = deepCopy(obj[attr]);        }        return copy;    }    throw new Error("Unable to copy obj! Its type isn't supported.");}var clone = deepCopy(customer) as Customer;alert(clone.name + ' ' + clone.example.type); // David DavidType// alert(clone.greet()); // Not OK - not really a customerclone.name = 'Steve';clone.example.type = 'SteveType';alert(customer.name + ' ' + customer.example.type); // David DavidType


1.Use spread operator

const obj1 = { param: "value" };const obj2 = { ...obj1 };

Spread operator takes all fields from obj1 and spread them over obj2. In the result you get new object with new reference and the same fields as original one.

Remember that it is shallow copy, it means that if object is nested then its nested composite params will exists in the new object by the same reference.

2.Object.assign()

const obj1={ param: "value" };const obj2:any = Object.assign({}, obj1);

Object.assign create real copy, but only own properties, so properties in prototype will not exist in copied object. It is also shallow copy.


3.Object.create()

const obj1={ param: "value" };const obj2:any = Object.create(obj1);

Object.create is not doing real cloning, it is creating object from prototype. So use it if the object should clone primary type properties, because primary type properties assignment is not done by reference.

Pluses of Object.create are that any functions declared in prototype will be available in our newly created object.


Few things about shallow copy

Shallow copy puts into new object all fields of the old one, but it also means that if original object has composite type fields (object, arrays etc.) then those fields are put in new object with the same references. Mutation such field in original object will be reflected in new object.

It maybe looks like a pitfall, but really situation when the whole complex object needs to be copied is rare. Shallow copy will re-use most of memory which means that is very cheap in comparison to deep copy.


Deep copy

Spread operator can be handy for deep copy.

const obj1 = { param: "value", complex: { name: "John"}}const obj2 = { ...obj1, complex: {...obj1.complex}};

Above code created deep copy of obj1. Composite field "complex" was also copied into obj2. Mutation field "complex" will not reflect the copy.


Try this:

let copy = (JSON.parse(JSON.stringify(objectToCopy)));

It is a good solution until you are using very large objects or your object has unserializable properties.

In order to preserve type safety you could use a copy function in the class you want to make copies from:

getCopy(): YourClassName{    return (JSON.parse(JSON.stringify(this)));}

or in a static way:

static createCopy(objectToCopy: YourClassName): YourClassName{    return (JSON.parse(JSON.stringify(objectToCopy)));}