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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34201483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:03:07  来源:igfitidea点击:

Deep clone in TypeScript (preserving types)

javascripttypescriptlodash

提问by Julian B

I need to deep clone an object in TypeScript. This shouldn't be a problem as libraries like Lodash provide appropriate functions for that. However, these seem to discard type information.

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

> 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 中的对象?

回答by Retsam

Typescript isn't discarding type information here. In the DefinitelyTyped lodash.d.tsfile, you can see that cloneDeepis defined as

Typescript 不会在这里丢弃类型信息。在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 Tas input, and spits out a Tas output. So Typescript isn't losing any type information; it considers the output of cloneDeepto be the same type as the input.

忽略我们不关心的参数,它将 aT作为输入,并吐出 aT作为输出。所以 Typescript 不会丢失任何类型信息;它认为 的输出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 typeofnot working as you expect? It's because the Typescript type information doesn't carry over to runtime. instanceofis a native JS operator, which Typescript doesn't change the behavior of, which you can see by running this snippet:

那么为什么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>

The reason that b instanceof Ais false is that instanceofis checking against constructors: x instanceof Areturns true if the function Ais a constructor somewhere in x's prototype chain (see the MDN documentation on instanceof). Lodash, however, doesn't use constructors when it clones objects. 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.

b instanceof A错误的原因instanceof是检查构造函数:x instanceof A如果函数A是 x 原型链中某处的构造函数,则返回 true (请参阅有关instanceof的 MDN 文档)。然而,Lodash 在克隆对象时不使用构造函数。它不能。(它怎么知道要传递什么参数?)它创建一个普通的 JS 对象,该对象具有克隆对象的所有方法,但不重现它的原型链。

Lodash's clone(and most of lodash's methods, really) is best used when dealing with raw JS Objects. If you're using it in conjunction with constructors and instanceofchecking things get a bit murky.

clone在处理原始 JS 对象时,最好使用Lodash (以及大多数 lodash 的方法)。如果您将它与构造函数结合使用并且instanceof检查事情会变得有点模糊。



One solution here is to avoid the instanceofchecking, and do something akin to duck typing; 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.

这里的一种解决方案是避免instanceof检查,并做一些类似于鸭子打字的事情;不要检查对象的构造函数是否是特定函数,而是检查对象是否具有您期望的属性。

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

回答by Martin Vseticka

There is an interesting blog post about deep cloning http://blog.soulserv.net/understanding-object-cloning-in-javascript-part-ii/. You can use the author's implementation of the deep cloning clone()function:

有一篇关于深度克隆的有趣博客文章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]

[ clone() 源代码] [完整的源代码]

回答by Julian Torregrosa

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

回答by Radouane ROUFID

You can use Lodash#cloneDeeputility. Example of use :

您可以使用Lodash#cloneDeep实用程序。使用示例:

import * as _ from "lodash";

...

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