TypeScript中的深度克隆(保留类型)

时间:2015-12-10 12:06:05

标签: javascript typescript lodash

我需要在TypeScript中深度克隆一个对象。这不应该是一个问题,因为像Lodash这样的库为它提供了适当的功能。但是,这些似乎丢弃了类型信息。

> var a = new SomeClass();
> a instanceof SomeClass;
< true
> var b = _.cloneDeep(a);
> b instanceof SomeClass;
< false

有没有办法在保留此输入信息的同时克隆TypeScript中的对象?

4 个答案:

答案 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

[clone() source code] [Complete source code]

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