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

7voto

Decade Moon Punkte 32128

Sie können auch etwas wie dies haben:

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

Stellen Sie nur sicher, dass Sie die Methode clone in allen Unterklassen von Entity überschreiben, sonst enden Sie mit teilweisen Kopien.

Der Rückgabetyp von this wird immer dem Typ der Instanz entsprechen.

4voto

pablorsk Punkte 3195

Wenn Sie diesen Fehler erhalten:

TypeError: this.constructor(...) ist keine Funktion

Dies ist das korrekte Skript:

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

4voto

Valeriy Katkov Punkte 22434

Seit der Veröffentlichung von TypeScript 3.7 werden nun rekursive Typaliasen unterstützt und ermöglichen es uns, eine typsichere deepCopy() Funktion zu definieren:

// Der DeepCopy-Typ kann leicht um andere Typen erweitert werden,
// wie z.B. Set & Map, wenn die Implementierung sie unterstützt.
type DeepCopy =
    T extends undefined | null | boolean | string | number ? T :
    T extends Function | Set | Map ? unknown :
    T extends ReadonlyArray ? Array> :
    { [K in keyof T]: DeepCopy };

function deepCopy(obj: T): DeepCopy {
    // Die Implementierung ist nicht wichtig, einfach die einfachste verwenden
    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); // keine Fehler

declare const uncopiableUser: UncopiableUser;
const uncopiableUserCopy: UncopiableUser = deepCopy(uncopiableUser); // Kompilierfehler

Spielplatz

3voto

marckassay Punkte 1229

Hier ist mein Mash-up! Und hier ist ein StackBlitz Link dazu. Es ist derzeit nur auf das Kopieren von einfachen Typen und Objekttypen beschränkt, aber ich denke, es könnte leicht modifiziert werden.

   let deepClone = (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;
    };

3voto

SOUVIK SAHA Punkte 191

Sie könnten Destrukturierungszuweisung mit Spread-Syntax verwenden:

var obj = {id = 1, name = 'product1'};
var clonedObject = {...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