tutorial react nodejs example es6 ejemplos javascript typescript

javascript - react - mecanografiado-objeto de clonación



typescript vs es6 (19)

Tengo una superclase que es la principal ( Entity ) para muchas subclases ( Customer , Product , ProductCategory ...)

Estoy buscando clonar dinámicamente un objeto que contiene diferentes subobjetos en Typecript.

Por ejemplo: un Customer que tiene un Product diferente que tiene una ProductCategory

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

Para clonar todo el árbol de objetos, creé una función en 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; }

El new genera el siguiente error cuando se transpila a javascript: error TS2351: Cannot use ''new'' with an expression whose type lacks a call or construct signature.

Aunque el script funciona, me gustaría deshacerme del error transpilado.


Resolviendo el problema específico

Puede usar una aserción de tipo para decirle al compilador que conoce mejor:

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

Clonación

Tenga en cuenta que a veces es mejor escribir su propio mapeo, en lugar de ser totalmente dinámico. Sin embargo, hay algunos trucos de "clonación" que puedes usar que te dan efectos diferentes.

Usaré el siguiente código para todos los ejemplos posteriores:

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

Opción 1: propagación

Propiedades:
Métodos: no
Copia profunda: 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

Opción 2: Object.assign

Propiedades:
Métodos: no
Copia profunda: 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

Opción 3: Object.create

Propiedades:
Métodos:
Copia profunda: 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

Opción 4: Función de copia profunda

Propiedades:
Métodos: no
Copia profunda:

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 = <Customer>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


¡Aquí está mi mash-up! Y aquí hay un enlace de StackBlitz . Actualmente se limita a copiar solo tipos simples y tipos de objetos, pero podría modificarse fácilmente, creo.

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


¿Qué tal el viejo jQuery? Aquí está el clon profundo:

var clone = $.extend(true, {}, sourceObject);


1.Utilice el operador de propagación

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

El operador de propagación toma todos los campos de obj1 y los extiende sobre obj2. En el resultado, obtienes un nuevo objeto con una nueva referencia y los mismos campos que el original.

Recuerde que es una copia superficial, significa que si el objeto está anidado, sus parámetros compuestos anidados existirán en el nuevo objeto con la misma referencia.

2.Object.assign ()

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

Object.assign crea una copia real, pero solo propiedades propias, por lo que las propiedades en el prototipo no existirán en el objeto copiado. También es una copia superficial.

3.Object.create ()

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

Object.create no está haciendo una clonación real , está creando un objeto a partir del prototipo. Úselo si el objeto debe clonar propiedades de tipo primario, porque la asignación de propiedades de tipo primario no se realiza por referencia.

Las ventajas de Object.create son que cualquier función declarada en prototipo estará disponible en nuestro objeto recién creado.

Pocas cosas sobre copia superficial

La copia superficial coloca en un objeto nuevo todos los campos del anterior, pero también significa que si el objeto original tiene campos de tipo compuesto (objeto, matrices, etc.), esos campos se colocan en un objeto nuevo con las mismas referencias. La mutación de dicho campo en el objeto original se reflejará en el nuevo objeto.

Quizás parezca una trampa, pero la situación en la que todo el objeto complejo necesita ser copiado es rara. La copia superficial reutilizará la mayor parte de la memoria, lo que significa que es muy barata en comparación con la copia profunda.

Copia profunda

El operador de propagación puede ser útil para la copia profunda.

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

El código anterior creó una copia profunda de obj1. El campo compuesto "complejo" también se copió en obj2. El campo de mutación "complejo" no reflejará la copia.


Agregue "lodash.clonedeep": "^4.5.0" a su package.json . Luego use así:

import * as _ from ''lodash''; ... const copy = _.cloneDeep(original)


En typeScript pruebo con angular, y está funcionando bien

public clone(toClone: any): any { const newClone = {}; for (const attribut in toClone) { if (typeof toClone[attribut] === ''object'') { newClone[attribut] = this.clone(toClone[attribut]); } else { newClone[attribut] = toClone[attribut]; } } return newClone; }


Encontré este problema yo mismo y al final escribí una pequeña biblioteca cloneable-ts que proporciona una clase abstracta, que agrega un método de clonación a cualquier clase que la extienda. La clase abstracta toma prestada la función de copia profunda descrita en la respuesta aceptada por Fenton solo reemplazando copy = {}; con copy = Object.create(originalObj) para preservar la clase del objeto original. Aquí hay un ejemplo del uso de la clase.

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<TestArgs> implements CloneableArgs<PersonArgs> { 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 simplemente puede usar el método auxiliar 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


Es fácil obtener una copia superficial con "Object Spread" introducido en TypeScript 2.1

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

produce este 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


Intenté crear un servicio genérico de copia / clonación que retiene los tipos de objetos anidados. Me encantaría recibir comentarios si estoy haciendo algo mal, pero parece funcionar hasta ahora ...

import { Injectable } from ''@angular/core''; @Injectable() export class CopyService { public deepCopy<T>(objectToClone: T): T { // If it''s a simple type or null, just return it. if (typeof objectToClone === ''string'' || typeof objectToClone === ''number'' || typeof objectToClone === ''undefined'' || typeof objectToClone === ''symbol'' || typeof objectToClone === ''function'' || typeof objectToClone === ''boolean'' || objectToClone === null ) { return objectToClone; } // Otherwise, check if it has a constructor we can use to properly instantiate it... let ctor = Object.getPrototypeOf(objectToClone).constructor; if (ctor) { let clone = new ctor(); // Once we''ve instantiated the correct type, assign the child properties with deep copies of the values Object.keys(objectToClone).forEach(key => { if (Array.isArray(objectToClone[key])) clone[key] = objectToClone[key].map(item => this.deepCopy(item)); else clone[key] = this.deepCopy(objectToClone[key]); }); if (JSON.stringify(objectToClone) !== JSON.stringify(clone)) console.warn(''object cloned, but doesnt match exactly.../nobject: '' + JSON.stringify(objectToClone) + "/nclone: " + JSON.stringify(clone)) // return our cloned object... return clone; } else { //not sure this will ever get hit, but figured I''d have a catch call. console.log(''deep copy found something it didnt know: '' + JSON.stringify(objectToClone)); return objectToClone; } } }


Mi opinión sobre esto:

Object.assign(...) solo copia propiedades y perdemos el prototipo y los métodos.

Object.create(...) no está copiando propiedades para mí y solo está creando un prototipo.

Lo que funcionó para mí es crear un prototipo usando Object.create(...) y copiar propiedades a él usando Object.assign(...) :

Entonces, para un objeto foo , haga un clon como este:

Object.assign(Object.create(foo), foo)


Para clones profundos serializables, con información de tipo es,

export function clone<T>(a: T): T { return JSON.parse(JSON.stringify(a)); }


Para la clonación profunda de un objeto que puede contener otros objetos, matrices, etc., uso:

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] = this.deepCopy(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = this.deepCopy(obj[attr]); } return copy; } throw new Error("Unable to copy obj! Its type isn''t supported."); }

Utilizar:

Object.keys(source).forEach((key) => { copy[key] = source[key] })


Para un clon simple del contenido del objeto del agujero, simplemente encadenar y analizar la instancia:

let deepClone = <T>(source: T): { [k: string]: any } => { let results: { [k: string]: any } = {}; for (let P in source) { if (typeof source[P] === ''object'') { results[P] = deepClone(source[P]); } else { results[P] = source[P]; } } return results; };

Mientras que cambio los datos en el árbol objectToClone, no hay ningún cambio en cloneObject. Ese fue mi requisito.

Espero que ayude


Prueba esto:

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

Es una buena solución hasta que esté utilizando objetos muy grandes o su objeto tenga propiedades no serializables.

Para preservar la seguridad de tipos, puede usar una función de copia en la clase de la que desea hacer copias:

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

o de forma estática:

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


Si obtiene este error:

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

Este es el script correcto:

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


Si ya tiene el objeto de destino, por lo que no desea crearlo de nuevo (como si estuviera actualizando una matriz), debe copiar las propiedades.
Si lo ha hecho de esta manera:

Object.keys(source).forEach((key) => { copy[key] = source[key] })

Se deben alabar. (mira el título "versión 2")


También puedes tener algo como esto:

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; } }

Solo asegúrese de anular el método de clone en todas las subclases de entidades, de lo contrario, terminará con clones parciales.

El tipo de retorno de this siempre coincidirá con el tipo de la instancia.


Terminé haciendo:

public clone(): any { const result = new (<any>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; }

Porque:

var cloneObj = new (<any>this.constructor());

de @Fenton dio errores de tiempo de ejecución.

Versión mecanografiada: 2.4.2


TypeScript / Javascript tiene su propio operador para la clonación superficial:

let shallowClone = { ...original };