TypeScriptでオブジェクトをディープクローンする必要があります。 Lodashのようなライブラリーが適切な機能を提供するため、これは問題になりません。ただし、これらは型情報を破棄するようです。
> var a = new SomeClass();
> a instanceof SomeClass;
< true
> var b = _.cloneDeep(a);
> b instanceof SomeClass;
< false
この入力情報を保持しながら、TypeScriptでオブジェクトを複製する方法はありますか?
TypeScriptはここで型情報を破棄しません。 DefinitelyTyped lodash.d.ts ファイルでは、cloneDeep
が次のように定義されていることがわかります。
cloneDeep<T>(
val: T,
customizer?: (value: any) => any,
thisArg?: any
) : T
気にしない引数は無視して、入力としてT
を受け取り、出力としてT
を吐き出します。したがって、TypeScriptが型情報を失うことはありません。 cloneDeep
の出力は入力と同じタイプであると見なされます。
エディターでこれを確認できるはずです。変数のタイプやオートコンプリートメソッドを検査できるエディターがあると仮定すると(そうでない場合は強くお勧めします)。
なぜtypeof
が期待どおりに機能しないのですか?これは、TypeScript型情報がランタイムに引き継がれないためです。 instanceof
はネイティブのJS演算子であり、TypeScriptは動作を変更しません。次のスニペットを実行することで確認できます。
"use strict";
class A {}
let a = new A();
let b = _.cloneDeep(a);
if (b instanceof A) {
alert("b is an instance of A");
} else {
alert("b is not an instance of A");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
b instanceof A
がfalseの場合、instanceof
はコンストラクターに対してチェックを行います:x instanceof A
は、関数A
がxのプロトタイプチェーンのどこかのコンストラクターである場合にtrueを返します( instanceof のMDNドキュメントを参照)。ただし、Lodashはオブジェクトを複製するときにコンストラクターを使用しません。できません。 (どの引数を渡すかをどのように知るのでしょうか?)クローンオブジェクトのすべてのメソッドを含むプレーンなJSオブジェクトを作成しますが、プロトタイプチェーンを再現しません。
Lodashのclone
(および実際にはほとんどのlodashのメソッド)は、生のJSオブジェクトを扱う場合に最適に使用されます。コンストラクターとinstanceof
を組み合わせて使用している場合、物事の確認は少し曖昧になります。
ここでの解決策の1つは、instanceof
チェックを回避し、ダックタイピングに似た何かをすることです。オブジェクトのコンストラクターが特定の関数であることを確認するのではなく、オブジェクトが期待するプロパティを持っていることを確認してください。
別の解決策は、コメントで示唆されているように、lodashを使用しないクラス自体にcloneメソッドを実装することです。
class A() {
clone() {
var cloned = new A(); //pass appropriate constructor args
//make other necessary changes to make the state match
return cloned;
}
}
ディープクローニングに関する興味深いブログ投稿があります http://blog.soulserv.net/understanding-object-cloning-in-javascript-part-ii/ 。ディープクローニングclone()
関数の作成者の実装を使用できます。
class SomeClass {
constructor(public test) {
}
}
let c = new SomeClass("Hey!");
c.test = "Hey!";
console.log(c instanceof SomeClass); // returns true
let cloneC = clone(c, /* resolve circular references */ true);
console.log(cloneC instanceof SomeClass); // returns true
[ clone()ソースコード ] [ 完全なソースコード ]
Lodash#cloneDeep ユーティリティを使用できます。使用例:
import * as _ from "lodash";
...
{
this.cloned = _.cloneDeep(data);
}
JSON stringifyおよび解析メソッドを使用して、独自のディープクローンを作成できます。
export function deepClone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj)) as T;
}