简体   繁体   English

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

[英]Deep clone in TypeScript (preserving types)

I need to deep clone an object in TypeScript. 我需要在TypeScript中深度克隆一个对象。 This shouldn't be a problem as libraries like Lodash provide appropriate functions for that. 这应该不是问题,因为像Lodash这样的库提供了适当的功能。 However, these seem to discard type information. 但是,这些似乎丢弃了类型信息。

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

Is there a way to clone objects in TypeScript while preserving this typing information? 有没有办法在保留此类型信息的同时克隆TypeScript中的对象?

Typescript isn't discarding type information here. 打字稿不会丢弃类型信息。 In the DefinitelyTyped lodash.d.ts file, you can see that cloneDeep is defined as 在DefinitelyTyped lodash.d.ts文件中,您可以看到cloneDeep被定义为

cloneDeep<T>(
    val: T,
    customizer?: (value: any) => any,
    thisArg?: any
) : T

Ignoring the arguments we don't care about, it takes a T as input, and spits out a T as output. 忽略我们不关心的参数,它需要一个T作为输入,并吐出一个T作为输出。 So Typescript isn't losing any type information; 因此,Typescript不会丢失任何类型的信息; it considers the output of cloneDeep to be the same type as the input. 它认为cloneDeep的输出与输入的类型相同。

You should be able to verify this via your editor: assuming you have some editor that lets you inspect the type of variables or autocompletes methods (which I'd highly recommend, if you don't). 您应该能够通过编辑器验证这一点:假设您有一些编辑器可以让您检查变量的类型或自动填充方法(如果您不这样做,我强烈建议您这样做)。


Why then is the typeof not working as you expect? 为什么typeof不能像你期望的那样工作? It's because the Typescript type information doesn't carry over to runtime. 这是因为Typescript类型信息不会延续到运行时。 instanceof is a native JS operator, which Typescript doesn't change the behavior of, which you can see by running this snippet: 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> 

The reason that b instanceof A is false is that instanceof is checking against constructors: x instanceof A returns true if the function A is a constructor somewhere in x's prototype chain (see the MDN documentation on instanceof ). b instanceof A为false的原因是instanceof正在检查构造函数:如果函数A是x的原型链中的某个构造函数Ax instanceof A返回true(请参阅instanceof上的MDN文档)。 Lodash, however, doesn't use constructors when it clones objects. 但是,Lodash在克隆对象时不使用构造函数。 It can't. 它不能。 (How would it know what arguments to pass?) It creates a plain JS object that has all the methods of the cloned object, but doesn't reproduce it's prototype chain. (它如何知道要传递的参数?)它创建了一个普通的JS对象,它具有克隆对象的所有方法,但不会重现它的原型链。

Lodash's clone (and most of lodash's methods, really) is best used when dealing with raw JS Objects. 在处理原始JS对象时,最好使用Lodash的clone (以及大多数lodash的方法)。 If you're using it in conjunction with constructors and instanceof checking things get a bit murky. 如果你将它与构造函数和instanceof一起使用,那么事情会变得有些模糊。


One solution here is to avoid the instanceof checking, and do something akin to duck typing; 这里的一个解决方案是避免检查instanceof ,并做类似鸭子打字的事情; don't check that the object's constructor is a particular function, but check that the object has the properties that you expect it to. 不要检查对象的构造函数是否是特定函数,但检查该对象是否具有您期望的属性。

Another solution is, as suggested in the comments, implement a clone method on your class itself, which wouldn't use lodash. 另一种解决方案是,如评论中所建议的,在您的类本身上实现克隆方法,该方法不会使用lodash。

class A() {
    clone() {
        var cloned = new A(); //pass appropriate constructor args
        //make other necessary changes to make the state match
        return cloned;
    }
}

There is an interesting blog post about deep cloning http://blog.soulserv.net/understanding-object-cloning-in-javascript-part-ii/ . 有一篇关于深度克隆的有趣博客文章http://blog.soulserv.net/understanding-object-cloning-in-javascript-part-ii/ You can use the author's implementation of the deep cloning clone() function: 您可以使用作者的深度克隆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 ] [ clone()源代码 ] [ 完整源代码 ]

You can use Lodash#cloneDeep utility. 您可以使用Lodash#cloneDeep实用程序。 Example of use : 使用示例:

import * as _ from "lodash";

...

{
    this.cloned = _.cloneDeep(data);
}

You can create your own deep clone using JSON stringify and parse methods: 您可以使用JSON stringify和parse方法创建自己的深度克隆:

export function deepClone<T>(obj: T): T {
  return JSON.parse(JSON.stringify(obj)) as T;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM