1. ホーム
  2. ジャバスクリプト

[解決済み】タイプスクリプト - オブジェクトのクローンを作成する

2022-03-29 07:36:05

質問

親となるスーパークラスがあるのですが( Entity ) のサブクラス ( Customer , Product , ProductCategory ...)

Typescript で、異なるサブオブジェクトを含むオブジェクトを動的にクローンすることを検討しています。

例:ある Customer を持つ、異なる Product を持っている人 ProductCategory

var cust:Customer  = new Customer ();

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

オブジェクトのツリー全体をクローンするために、以下の関数を作成しました。 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;
}

new は、javascriptにトランスパイルされると、以下のようなエラーが発生します。 error TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

スクリプトは動作するものの トランスパイルエラーをなくしたいです。

解決方法を教えてください。

具体的な問題解決

タイプアサーションを使用することで、コンパイラに自分の知識があることを伝えることができます。

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

クローン作成

時には、完全にダイナミックにマッピングするよりも、独自のマッピングを書いた方が良い場合があることを心に留めておいてください。しかし、いくつかの「クローン」トリックを使用することで、異なる効果を得ることができます。

この後の例では、すべて次のコードを使用します。

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


オプション1:スプレッド

プロパティー はい

メソッドです。なし

ディープコピー いいえ

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


オプション2:Object.assign

プロパティです。 はい

メソッドです。なし

ディープコピー いいえ

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


オプション3:Object.create

プロパティを指定します。 継承される

メソッドです。 継承される

ディープコピー シャロー・インヘリテット (深い変更はオリジナルとクローンの両方に影響する)

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


オプション4:ディープコピー機能

プロパティーです。 はい

メソッドです。なし

ディープコピー はい

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