typescript – object di clonazione

Ho una super class che è il genitore ( Entity ) per molte sottoclassi ( Customer , Product , categoria Product …)

Sto cercando di clonare dynamicmente un object che contiene diversi oggetti secondari in Typescript.

Ad esempio: un Customer che ha un Product diverso che ha una categoria di Product

 var cust:Customer = new Customer (); cust.name = "someName"; cust.products.push(new Product(someId1)); cust.products.push(new Product(someId2)); 

Per clonare l’intero albero dell’object ho creato una funzione in Entity

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

Il new aumenta il seguente errore quando viene trasposto in javascript: error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

Sebbene la sceneggiatura funzioni , mi piacerebbe sbarazzarmi dell’errore transpiled

Risolvere il problema specifico

Puoi usare un tipo di asserzione per dire al compilatore che conosci meglio:

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

clonazione

Tieni a mente che a volte è meglio scrivere la tua mapping, piuttosto che essere totalmente dinamico. Tuttavia, ci sono alcuni trucchi di “clonazione” che puoi usare che ti danno effetti di differenza.

Userò il seguente codice per tutti gli esempi seguenti:

 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')); 

Opzione 1: diffusione

Proprietà: Metodi: No Copia profonda: No

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

Opzione 2: Object.assign

Proprietà: Metodi: No Copia profonda: No

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

Opzione 3: Object.create

Proprietà: Metodi: Copia profonda: No

 var clone = Object.create(customer); alert(clone.name + ' ' + clone.example.type); // David DavidType alert(clone.greet()); // OK clone.name = 'Steve'; clone.example.type = 'SteveType'; alert(customer.name + ' ' + customer.example.type); // David SteveType 

Opzione 4: Funzione Deep Copy

Proprietà: Metodi: No Copia profonda:

 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); alert(clone.name + ' ' + clone.example.type); // David DavidType // alert(clone.greet()); // Not OK - not really a customer clone.name = 'Steve'; clone.example.type = 'SteveType'; alert(customer.name + ' ' + customer.example.type); // David DavidType 

1. Utilizzare l’operatore di spread

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

L’operatore di spread prende tutti i campi da obj1 e li distribuisce su obj2. Nel risultato ottieni un nuovo object con un nuovo riferimento e gli stessi campi di quello originale.

Ricorda che è una copia superficiale, significa che se l’object è annidato, i suoi parametri compositi nidificati saranno presenti nel nuovo object con lo stesso riferimento.

2.Object.assign ()

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

Object.assign crea una copia reale, ma possiede solo proprietà, quindi le proprietà nel prototipo non esisteranno nell’object copiato. È anche una copia poco profonda.


3.Object.create ()

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

Object.create non sta facendo la vera clonazione , sta creando oggetti dal prototipo. Quindi, utilizzalo se l’object deve clonare le proprietà del tipo principale, poiché l’assegnazione delle proprietà del tipo primario non viene eseguita per riferimento.

I plus di Object.create sono che tutte le funzioni dichiarate in prototipo saranno disponibili nel nostro object appena creato.


Poche cose sulla copia superficiale

La copia superficiale inserisce nel nuovo object tutti i campi del vecchio, ma significa anche che se l’object originale ha campi di tipo composito (object, array, ecc.) Questi campi vengono messi in un nuovo object con gli stessi riferimenti. La mutazione di tale campo nell’object originale si rifletterà nel nuovo object.

Può sembrare un trabocchetto, ma la situazione reale in cui l’intero object complesso deve essere copiato è rara. La copia superficiale riutilizzerà la maggior parte della memoria, il che significa che è molto economico rispetto alla copia profonda.


Copia profonda

L’operatore di diffusione può essere utile per la copia profonda.

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

Sopra codice creato copia profonda di obj1. Anche il “complesso” di campi compositi è stato copiato in obj2. Il campo delle mutazione “complesso” non rifletterà la copia.

Prova questo:

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

È una buona soluzione finché non utilizzi oggetti molto grandi o il tuo object ha proprietà non serializzabili.

Per preservare la sicurezza del tipo, puoi usare una funzione di copia nella class da cui vuoi fare delle copie:

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

o in modo statico:

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

È facile ottenere una copia superficiale con “Object Spread” introdotto in TypeScript 2.1

questo TypeScript: let copy = { ...original };

produce questo JavaScript:

 var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; var copy = __assign({}, original); 

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

Typescript / Javascript ha il proprio operatore per la clonazione superficiale:

 let shallowClone = { ...original }; 

Puoi anche avere qualcosa di simile a questo:

 class Entity { id: number; constructor(id: number) { this.id = id; } clone(): this { return new (this.constructor as typeof Entity)(this.id) as this; } } class Customer extends Entity { name: string; constructor(id: number, name: string) { super(id); this.name = name; } clone(): this { return new (this.constructor as typeof Customer)(this.id, this.name) as this; } } 

Assicurati solo di sovrascrivere il metodo clone in tutte le sottoclassi di Entity altrimenti finirai con cloni parziali.

Il tipo di ritorno di this corrisponderà sempre al tipo dell’istanza.

Se ottieni questo errore:

 TypeError: this.constructor(...) is not a function 

Questo è lo script corretto:

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

Per un semplice clone del contenuto dell’object del foro, ho semplicemente corretto l’analisi e l’analisi dell’istanza:

 let cloneObject = JSON.parse(JSON.stringify(objectToClone)) 

Mentre cambio i dati nella struttura objectToClone, non c’è alcun cambiamento in cloneObject. Quello era il mio requierement.

Spero che aiuti

È venuto da me questo problema e alla fine ho scritto una piccola libreria cloneable-ts che fornisce una class astratta, che aggiunge un metodo clone a qualsiasi class che lo estende. La class astratta prende in prestito la funzione Deep Copy descritta nella risposta accettata da Fenton sostituendo solo copy = {}; con copy = Object.create(originalObj) per preservare la class dell’object originale. Ecco un esempio di utilizzo della class.

 import {Cloneable, CloneableArgs} from 'cloneable-ts'; // Interface that will be used as named arguments to initialize and clone an object interface PersonArgs { readonly name: string; readonly age: number; } // Cloneable abstract class initializes the object with super method and adds the clone method // CloneableArgs interface ensures that all properties defined in the argument interface are defined in class class Person extends Cloneable implements CloneableArgs { readonly name: string; readonly age: number; constructor(args: TestArgs) { super(args); } } const a = new Person({name: 'Alice', age: 28}); const b = a.clone({name: 'Bob'}) a.name // Alice b.name // Bob b.age // 28 

O potresti semplicemente usare il metodo helper Cloneable.clone :

 import {Cloneable} from 'cloneable-ts'; interface Person { readonly name: string; readonly age: number; } const a: Person = {name: 'Alice', age: 28}; const b = Cloneable.clone(a, {name: 'Bob'}) a.name // Alice b.name // Bob b.age // 28 

Ho finito per fare:

 public clone(): any { const result = new (this.constructor); // some deserialization code I hade in place already... // which deep copies all serialized properties of the // object graph // result.deserialize(this) // you could use any of the usggestions in the other answers to // copy over all the desired fields / properties return result; } 

Perché:

 var cloneObj = new (this.constructor()); 

da @Fenton ha fornito errori di runtime.

Versione typescript: 2.4.2

semplicemente stringi l’object e poi esegui il reparse su un nuovo object.

 cloneObject(object: any): any { const jsonString = JSON.stringify(object); const clonedObject = JSON.parse(jsonString); return clonedObject; }