381 Stimmen

Typescript - Objekt klonen

Ich habe eine Superklasse, die das Elternelement (Entity) für viele Unterklassen (Customer, Product, ProductCategory...) ist

Ich möchte in Typescript dynamisch ein Objekt klonen, das verschiedene Teilobjekte enthält.

Zum Beispiel: ein Customer, der verschiedene Product enthält, die eine ProductCategory haben

var cust:Customer = new Customer();

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

Um den gesamten Objektbaum zu klonen, habe ich eine Funktion in Entity erstellt

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

Der new Fehler tritt auf, wenn er zu Javascript transpiliert wird: Fehler TS2351: Kann 'new' nicht mit einem Ausdruck verwenden, dessen Typ über eine Aufruf- oder Konstruktions-Signatur verfügt.

Obwohl das Skript funktioniert, möchte ich den transpilierten Fehler loswerden

3voto

blid Punkte 1141

Hier ist eine moderne Implementierung, die auch Set und Map berücksichtigt:

export function deepClone(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);
}

Es ausprobieren:

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}

2voto

Timur Osadchiy Punkte 5331

Bin auf dieses Problem gestoßen und habe am Ende eine kleine Bibliothek cloneable-ts geschrieben, die eine abstrakte Klasse bereitstellt, die einer Klasse ein Klonmethode hinzufügt, die diese erweitert. Die abstrakte Klasse entleiht die Deep Copy-Funktion, die in der akzeptierten Antwort von Fenton beschrieben ist, und ersetzt nur copy = {}; durch copy = Object.create(originalObj), um die Klasse des Originalobjekts beizubehalten. Hier ist ein Beispiel für die Verwendung der Klasse.

import {Cloneable, CloneableArgs} from 'cloneable-ts';

// Schnittstelle, die als benannte Argumente für die Initialisierung und das Klonen eines Objekts verwendet werden
interface PersonArgs {
    readonly name: string;
    readonly age: number;
}

// Cloneable abstrakte Klasse initialisiert das Objekt mit der Supermethode und fügt die Klonmethode hinzu
// CloneableArgs-Schnittstelle stellt sicher, dass alle in der Argumentsschnittstelle definierten Eigenschaften in der Klasse definiert sind
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

Oder Sie könnten einfach die Cloneable.clone Hilfsmethode verwenden:

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

2voto

Akhilesh Kumar Punkte 8683

Wenn Sie Rekursion + TypeScript + Generics verwenden möchten

const deepCopy = (obj: T): T => {
  // Überprüfen, ob der Wert ein Array oder ein Objekt ist
  if (typeof obj !== 'object' || obj === null) {
    return obj; // Wert zurückgeben, wenn obj kein Objekt ist
  }
  // Arrays behandeln
  if (Array.isArray(obj)) {
    return obj.map(item => deepCopy(item)) as unknown as T;
  }
  // Objekte behandeln
  const copy = {} as { [K in keyof T]: T[K] };
  Object.keys(obj).forEach(key => {
    copy[key as keyof T] = deepCopy((obj as { [key: string]: any })[key]);
  });
  return copy;
};

Bereinigter Code :P

const deepCopy = (obj: T): T => {
  if (typeof obj !== 'object' || obj === null) return obj;
  if (Array.isArray(obj)) return obj.map(item => deepCopy(item)) as unknown as T;
  const copy = {} as { [K in keyof T]: T[K] };
  Object.keys(obj).forEach(key => copy[key as keyof T] = deepCopy((obj as { [key: string]: any })[key]));
  return copy;
};

1voto

maty jimenez Punkte 11

In TypeScript teste ich mit Angular, und es funktioniert gut

deepCopy(obj) {

        var copy;

        // Handhaben der 3 einfachen Typen, und null oder undefined
        if (null == obj || "object" != typeof obj) return obj;

        // Handhaben von Date
        if (obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handhaben von Array
        if (obj instanceof Array) {
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = this.deepCopy(obj[i]);
            }
            return copy;
        }

        // Handhaben von 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("Kann Objekt nicht kopieren! Sein Typ wird nicht unterstützt.");
    }

1voto

ZiiMakc Punkte 21740

Zum Tiefenklonen eines Objekts, das andere Objekte, Arrays und so weiter enthalten kann, verwende ich:

const clone = (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(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(clonnedObj[prop])
    })

    return clonnedObj as T
  }

  return source
}

Verwenden Sie:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X