typescript - objet de clonage


208

J'ai une classe super qui est le parent ( Entity) pour de nombreux sous - classe ( Customer, Product, ProductCategory...)

Je cherche à cloner dynamiquement un objet qui contient différents sous-objets dans Typescript.

Par exemple: a Customerqui a différent Productqui a unProductCategory

var cust:Customer  = new Customer ();

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

Afin de cloner tout l'arbre d'objets, j'ai créé une fonction dans 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;
}

L' newerreur suivante augmente lorsqu'elle est transpilée en javascript:error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

Bien que le script fonctionne, je voudrais me débarrasser de l'erreur transpilée

Réponses:


276

Résoudre le problème spécifique

Vous pouvez utiliser une assertion de type pour indiquer au compilateur que vous connaissez mieux:

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

Clonage

Gardez à l'esprit qu'il est parfois préférable d'écrire votre propre cartographie plutôt que d'être totalement dynamique. Cependant, vous pouvez utiliser quelques astuces de «clonage» qui vous donnent des effets de différence.

J'utiliserai le code suivant pour tous les exemples suivants:

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: propagation

Propriétés: Oui
Méthodes: Non
Deep Copy: Non

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

Option 2: Object.assign

Propriétés: Oui
Méthodes: Non
Deep Copy: Non

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

Option 3: Object.create

Propriétés: Héritée
Méthodes: Héritée
copie complète : Shallow hérité (changements profonds affectent à la fois original et clone)

var clone = Object.create(customer);

alert(clone.name + ' ' + clone.example.type); // David DavidType
alert(clone.greet()); // OK

customer.name = 'Misha';
customer.example = new Example("MishaType");

// clone sees changes to original 
alert(clone.name + ' ' + clone.example.type); // Misha MishaType

clone.name = 'Steve';
clone.example.type = 'SteveType';

// original sees changes to clone
alert(customer.name + ' ' + customer.example.type); // Misha SteveType

Option 4: fonction de copie profonde

Propriétés: Oui
Méthodes: Non
Deep Copy: Oui

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 customer

clone.name = 'Steve';
clone.example.type = 'SteveType';

alert(customer.name + ' ' + customer.example.type); // David DavidType

Fermer, le transpile a cessé de se plaindre avec typescript 1.3, mais une fois en javascript, il générerait une erreur. Typescript 1.4.1, ne le lâchera pas.
David Laberge

1
Seriez-vous en mesure de préciser comment utilisez-vous exactement cela? J'ai inclus comme méthode de mon objet, puis j'ai eu une erreur disant que ce n'est pas une fonction ...
megalucio

1
J'obtiens l'erreur suivante: "ERROR TypeError: this.constructor (...) is not a constructor"
michali

3
Vous venez de faire un exemple public de ce client?
Blair Connolly

1
Quelqu'un peut-il TL; DR pour moi laquelle des solutions données dans toutes les réponses préserver le type OO du clone, c'est cloned instanceof MyClass === true-à- dire ?
Szczepan Hołyszewski le

192

1. utilisez l'opérateur de propagation

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

L'opérateur Spread prend tous les champs de obj1 et les répartit sur obj2. Dans le résultat, vous obtenez un nouvel objet avec une nouvelle référence et les mêmes champs que l'original.

Rappelez-vous qu'il s'agit d'une copie superficielle, cela signifie que si l'objet est imbriqué, ses paramètres composites imbriqués existeront dans le nouvel objet par la même référence.

2.Objet.assign ()

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

Object.assign crée une copie réelle, mais uniquement ses propres propriétés, de sorte que les propriétés du prototype n'existeront pas dans l'objet copié. C'est aussi une copie superficielle.


3.Objet.create ()

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

Object.create ne fait pas de vrai clonage , il crée un objet à partir d'un prototype. Utilisez-le donc si l'objet doit cloner les propriétés de type principal, car l'attribution des propriétés de type principal n'est pas effectuée par référence.

Les avantages d' Object.create sont que toutes les fonctions déclarées dans le prototype seront disponibles dans notre objet nouvellement créé.


Peu de choses sur la copie superficielle

La copie superficielle place dans le nouvel objet tous les champs de l'ancien, mais cela signifie également que si l'objet d'origine a des champs de type composite (objet, tableaux, etc.), ces champs sont placés dans un nouvel objet avec les mêmes références. La mutation d'un tel champ dans l'objet d'origine sera reflétée dans le nouvel objet.

Cela ressemble peut-être à un écueil, mais en réalité, la situation où tout l'objet complexe doit être copié est rare. La copie superficielle réutilisera la plupart de la mémoire, ce qui signifie qu'elle est très bon marché par rapport à la copie profonde.


Copie profonde

L'opérateur d'étalement peut être pratique pour la copie profonde.

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

Le code ci-dessus a créé une copie complète de obj1. Le champ composite «complexe» a également été copié dans obj2. Le champ de mutation "complexe" ne reflétera pas la copie.


8
Je ne pense pas que ce soit tout à fait correct. Object.create(obj1)crée un nouvel objet et affecte obj1 comme prototype. Aucun des champs de obj1 n'est copié ou cloné. Ainsi, les changements sur obj1 sans modifier obj2 seront visibles, car il n'a essentiellement aucune propriété. Si vous modifiez d'abord obj2, le prototype ne sera pas vu pour le champ que vous définissez car le champ d'obj2 avec le nom est plus proche dans la hiérarchie.
Ken Rimple

3
Vous verrez également ES2015 et les développeurs dactylographiés faire cela à la place, ce qui crée un objet à partir du premier paramètre (dans mon cas, un paramètre vide) et copie les propriétés du deuxième paramètre et des paramètres suivants): let b = Object.assign({}, a);
Ken Rimple

@KenRimple Vous avez raison à 100%, j'ai ajouté quelques informations supplémentaires.
Maciej Sikora


5
Object.assign créera des problèmes pour les objets profonds. Par exemple {nom: 'x', valeurs: ['a', 'b', 'c']}. Après avoir utilisé Object.assign pour cloner, les deux objets partagent le tableau de valeurs de sorte que la mise à jour de l'un affecte l'autre. Voir: developer.mozilla.org/en/docs/Web/JavaScript/Reference/… (section «Avertissement pour le clone profond»). Il dit: Pour le clonage profond, nous devons utiliser d'autres alternatives. En effet, Object.assign () copie la référence de propriété lorsque la propriété attribuée est un objet.
Meir

54

Essaye ça:

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

C'est une bonne solution jusqu'à ce que vous utilisiez des objets très volumineux ou que votre objet ait des propriétés non sérialisables.

Afin de préserver la sécurité des types, vous pouvez utiliser une fonction de copie dans la classe à partir de laquelle vous souhaitez effectuer des copies:

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

ou de manière statique:

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

5
C'est correct, mais vous devez garder à l'esprit que vous perdrez des informations sur le prototype et tous les types non pris en charge dans json lors de la sérialisation / analyse.
Stanislav E. Govorov

1
Cela semble également moins efficace que la fonction deepCopy fournie ci-dessus .
Mojtaba

J'ai cette erreur: "Conversion de la structure circulaire en JSON" lorsque j'utilise "(JSON.parse (JSON.stringify (objectToCopy)));"
Cedric Arnould

Ne fonctionne que dans 98% des cas. Peut conduire à des clés manquantes avec une undefinedvaleur, au moins. si objectToCopy = { x : undefined};puis après l'exécution de votre code Object.keys(objectToCopy).lengthest 1, while Object.keys(copy).lengthis 0.
Aidin le

36

TypeScript / JavaScript a son propre opérateur pour le clonage superficiel:

let shallowClone = { ...original };

15

Il est facile d'obtenir une copie superficielle avec "Object Spread" introduit dans TypeScript 2.1

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

produit ce 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


2
Remarque: cela va créer une copie superficielle
Jimmy Kane

13

Pour le clone profond sérialisable, avec les informations de type,

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

1
Cela peut changer l'ordre des accessoires. Juste un avertissement pour certaines personnes. De plus, il ne gère pas correctement les dates.
Pangamma

Cela peut changer l'ordre des accessoires - peut-être essayer npmjs.com/package/es6-json-stable-stringify au lieu deJSON.stringify
Polv

@Polv, si quelqu'un se fie à l'ordre des clés dans un objet, je pense qu'il a un plus gros problème que clone. :)
Aidin le

Cette solution peut manquer des clés avec une undefinedvaleur. Voir mon commentaire sur la réponse similaire ci-dessus: stackoverflow.com/questions/28150967/typescript-cloning-object/...
Aidin le

J'ai cependant dit explicitement «sérialisable». En outre, cela dépend du cas d'utilisation, mais je serais toujours heureux de jeter undefined (ce que je sais, est impossible dans les tableaux). Pour les dates et les RegExps, ou plus que cela (par exemple, la plupart des classes, la plupart des fonctions), je recommande les fonctions récursives - stackoverflow.com/questions/122102/…
Polv

7

Mon point de vue:

Object.assign(...) ne copie que les propriétés et nous perdons le prototype et les méthodes.

Object.create(...) ne copie pas les propriétés pour moi et crée simplement un prototype.

Ce qui a fonctionné pour moi, c'est de créer un prototype en utilisant Object.create(...)et en y copiant des propriétés en utilisant Object.assign(...):

Donc, pour un objet foo, faites un clone comme ceci:

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

Il se passe une chose très subtile ici. Vous êtes en train de foodevenir le parent prototypique du clonedFoo(nouvel objet). Bien que cela puisse sembler correct, vous devez garder à l'esprit qu'une propriété manquante sera recherchée dans la chaîne de prototypes, donc const a = { x: 8 }; const c = Object.assign(Object.create(a), a); delete c.x; console.log(c.x);imprime 8, alors que cela devrait l'être undefined! (Lien REPL: repl.it/repls/CompetitivePreemptiveKeygen )
Aidin le

De plus, si vous ajoutez ultérieurement une propriété à foo, elle apparaîtra automatiquement pour clonedFoo! par exemple foo.y = 9; console.log(clonedFoo.y), imprimera à la 9place de undefined. Il est fort probable que ce ne soit pas ce que vous demandez!
Aidin le

@Aidin Alors comment assurer une copie complète?
Muhammad Ali le

toute autre solution dans cette question, qui fait une copie par valeur de manière récursive (par exemple, stackoverflow.com/a/53025968 par marckassay) garantit que, puisqu'il n'y a aucune référence à l'objet source maintenu dans l'objet cible.
Aidin le

6

Vous pouvez également avoir quelque chose comme ceci:

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

Assurez-vous simplement de remplacer la cloneméthode dans toutes les Entitysous-classes, sinon vous vous retrouverez avec des clones partiels.

Le type de retour de thiscorrespondra toujours au type de l'instance.


5

Ajoutez "lodash.clonedeep": "^4.5.0"à votre package.json. Ensuite, utilisez comme ceci:

import * as _ from 'lodash';

...

const copy = _.cloneDeep(original)

Je me demande simplement s'il est acceptable d'utiliser une bibliothèque, si vous ne connaissez pas vraiment les implémentations / implications? (L'implémentation pour cloneDeep est github.com/lodash/lodash/blob/master/.internal/baseClone.js ) Je pense que les fonctions récursives qui touchent les propriétés non énumérables sont parmi les meilleures solutions. (Quelque part dans ce QA .)
Polv

3

Voici mon mash-up! Et voici un lien vers StackBlitz . Il est actuellement limité à la copie uniquement de types simples et de types d'objets, mais pourrait être modifié facilement, je pense.

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

1
Fonctionne assez bien pour autant que je puisse voir. Cependant, typeof nullest également un objet, la requête doit donc l'être à la if (source[P] !== null && typeof source[P] === 'object')place. Sinon, vos valeurs nulles seront transformées en un objet vide.
MortenMoulder

3

Si vous obtenez cette erreur:

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

C'est le bon script:

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

4
Est-ce correct cloneObj[attribut] = this.clone();? ou vous voulez direcloneObj[attribut] = this[attribut].clone();
Serginho

2

J'ai rencontré ce problème moi-même et à la fin j'ai écrit une petite bibliothèque clonable-ts qui fournit une classe abstraite, qui ajoute une méthode de clonage à toute classe l'étendant. La classe abstraite emprunte la fonction Deep Copy décrite dans la réponse acceptée par Fenton en remplaçant uniquement copy = {};par copy = Object.create(originalObj)pour conserver la classe de l'objet d'origine. Voici un exemple d'utilisation de la classe.

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

Ou vous pouvez simplement utiliser la Cloneable.cloneméthode d'assistance:

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    

2

Depuis la sortie de TypeScript 3.7, les alias de type récursifs sont désormais pris en charge et cela nous permet de définir une deepCopy()fonction de type sécurisé :

// DeepCopy type can be easily extended by other types,
// like Set & Map if the implementation supports them.
type DeepCopy<T> =
    T extends undefined | null | boolean | string | number ? T :
    T extends Function | Set<any> | Map<any, any> ? unknown :
    T extends ReadonlyArray<infer U> ? Array<DeepCopy<U>> :
    { [K in keyof T]: DeepCopy<T[K]> };

function deepCopy<T>(obj: T): DeepCopy<T> {
    // implementation doesn't matter, just use the simplest
    return JSON.parse(JSON.stringify(obj));
}

interface User {
    name: string,
    achievements: readonly string[],
    extras?: {
        city: string;
    }
}

type UncopiableUser = User & {
    delete: () => void
};

declare const user: User;
const userCopy: User = deepCopy(user); // no errors

declare const uncopiableUser: UncopiableUser;
const uncopiableUserCopy: UncopiableUser = deepCopy(uncopiableUser); // compile time error

Terrain de jeux


// compile time errorcar UncopiableUserc'est toujours agréable, mais dans quelle mesure s'applique-t-il aux solutions de fonctions récursives?
Polv

1

Voici une implémentation moderne qui tient compte Setet Mapaussi:

export function deepClone<T extends object>(value: T): T {
  if (typeof value !== 'object' || value === null) {
    return value;
  }

  if (value instanceof Set) {
    return new Set(Array.from(value, deepClone)) as T;
  }

  if (value instanceof Map) {
    return new Map(Array.from(value, ([k, v]) => [k, deepClone(v)])) as T;
  }

  if (value instanceof Date) {
    return new Date(value) as T;
  }

  if (value instanceof RegExp) {
    return new RegExp(value.source, value.flags) as T;
  }

  return Object.keys(value).reduce((acc, key) => {
    return Object.assign(acc, { [key]: deepClone(value[key]) });
  }, (Array.isArray(value) ? [] : {}) as T);
}

Essayer:

deepClone({
  test1: { '1': 1, '2': {}, '3': [1, 2, 3] },
  test2: [1, 2, 3],
  test3: new Set([1, 2, [1, 2, 3]]),
  test4: new Map([['1', 1], ['2', 2], ['3', 3]])
});

test1:
  1: 1
  2: {}
  3: [1, 2, 3]

test2: Array(3)
  0: 1
  1: 2
  2: 3

test3: Set(3)
  0: 1
  1: 2
  2: [1, 2, 3]

test4: Map(3)
  0: {"1" => 1}
  1: {"2" => 2}
  2: {"3" => 3}


0

Pour un simple clone du contenu de l'objet trou, je stringify et analyse simplement l'instance:

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

Alors que je change les données dans l'arborescence objectToClone, il n'y a pas de changement dans cloneObject. C'était ma demande.

J'espère que ça aide


1
Peut manquer des clés avec une undefinedvaleur. Voir mon commentaire sur la réponse similaire ci-dessus: stackoverflow.com/questions/28150967/typescript-cloning-object/…
Aidin le

0

J'ai fini par faire:

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

Car:

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

de @Fenton a donné des erreurs d'exécution.

Version dactylographiée: 2.4.2


0

Que diriez-vous du bon vieux jQuery?! Voici le clone profond:

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

Cette question n'était pas étiquetée JQuery et JQuery n'était pas mentionnée dans la question. Il serait également énorme d'inclure JQuery dans un projet juste pour faire un clonage profond.
LewisM

C'est assez juste, mais l'OP ne concerne pas la façon de cloner, il s'agit d'identifier un problème dans le code qu'il a fourni et vous avez répondu avec la méthode de clonage jQuery sans vraiment répondre à la question. Je ne suis pas celui qui vous a refusé, mais je pense que c'est peut-être pour cela que vous avez été rejeté.
LewisM

0

J'ai essayé de créer un service de copie / clonage générique qui conserve les types pour les objets imbriqués. J'adorerais les commentaires si je fais quelque chose de mal, mais cela semble fonctionner jusqu'à présent ...

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

0

Dans typeScript, je teste avec angular, et ça marche bien

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.");
    }

0

Pour le clonage en profondeur d'un objet pouvant contenir d'autres objets, tableaux, etc., j'utilise:

const clone = <T>(source: T): T => {
  if (source === null) return source

  if (source instanceof Date) return new Date(source.getTime()) as any

  if (source instanceof Array) return source.map((item: any) => clone<any>(item)) as any

  if (typeof source === 'object' && source !== {}) {
    const clonnedObj = { ...(source as { [key: string]: any }) } as { [key: string]: any }
    Object.keys(clonnedObj).forEach(prop => {
      clonnedObj[prop] = clone<any>(clonnedObj[prop])
    })

    return clonnedObj as T
  }

  return source
}

Utilisation:

const obj = {a: [1,2], b: 's', c: () => { return 'h'; }, d: null, e: {a:['x'] }}
const objClone = clone(obj)


-1
function instantiateEmptyObject(obj: object): object {
    if (obj == null) { return {}; }

    const prototype = Object.getPrototypeOf(obj);
    if (!prototype) {
        return {};
    }

    return Object.create(prototype);
}

function quickCopy(src: object, dest: object): object {
    if (dest == null) { return dest; }

    return { ...src, ...dest };
}

quickCopy(src, instantiateEmptyObject(new Customer()));

Cette réponse dans son état actuel n'est pas si utile. Pouvez-vous ajouter plus de détails sur la façon de l'utiliser pour résoudre le problème d'origine?
phsource

-2

Si vous avez déjà l'objet cible, vous ne voulez donc pas le créer à nouveau (comme si vous mettez à jour un tableau), vous devez copier les propriétés.
Si vous l'avez fait de cette façon:

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

La louange est due. (regardez le titre "version 2")


Les fonctions? Des tableaux? Objets de date? Préservation des types? Et bien sûr, qu'en est-il des objets? Si la fonction ci-dessus rencontre l'un des types ci-dessus, le clonage en profondeur échouera. Vous aurez copié les références aux mêmes données. Lorsqu'ils vont éditer les propriétés enfants de l'objet cloné, ils finiront également par éditer l'objet d'origine.
Pangamma
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.