我需要在TypeScript中深度克隆一个对象。这不应该是一个问题,因为像Lodash这样的库为它提供了适当的功能。但是,这些似乎丢弃了类型信息。
> var a = new SomeClass();
> a instanceof SomeClass;
< true
> var b = _.cloneDeep(a);
> b instanceof SomeClass;
< false
有没有办法在保留此输入信息的同时克隆TypeScript中的对象?
答案 0 :(得分:7)
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对象,它包含克隆对象的所有方法,但不会重现它的原型链。
当处理原始JS对象时,最好使用Lodash的clone
(以及大多数lodash的方法)。如果你将它与构造函数一起使用并且instanceof
检查事情会变得有点模糊。
这里的一个解决方案是避免instanceof
检查,并做类似鸭子打字的事情;不要检查对象的构造函数是否是特定函数,但检查对象是否具有您期望的属性。
另一种解决方案是,如评论中所建议的,在您的类本身上实现一个克隆方法,该方法不会使用lodash。
class A() {
clone() {
var cloned = new A(); //pass appropriate constructor args
//make other necessary changes to make the state match
return cloned;
}
}
答案 1 :(得分:5)
有一篇关于深度克隆的有趣博客文章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
答案 2 :(得分:1)
您可以使用Lodash#cloneDeep实用程序。使用示例:
import * as _ from "lodash";
...
{
this.cloned = _.cloneDeep(data);
}
答案 3 :(得分:1)
您可以使用JSON stringify和parse方法创建自己的深层克隆:
export function deepClone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj)) as T;
}