深度克隆只读实例

Deep cloning a readonly instance

假设我们有一个 class 和一个引用另一个 class 的 属性。我希望能够深度克隆它的 "Immutable"(或 Readonly)实例:

import * as _ from 'lodash'; 

interface IParent{
   name: string;
}

interface IChild{
   name: string;
   parent: IParent;
}

public class Child implements IChild{
   public name: string;
   public parent: string;

   constructor(props: IChild){
      Object.assign(this, props);
   }

   toImmutable(): Readonly<IChild> {
       return _.cloneDeep(this); //lodash deep clone
   }
}

虽然此代码在 child 实例 Readonly 上创建了第一个 class 属性,但仍然可以编辑引用的对象:

let child = new Child({ 
   name: 'abc',
   parent: { name: 123 }
});

let immutable = child.toImmutable();
immutable.name = 'abc';  //not allowed
immutable.parent.name = 'abc';  //this is allowed!!!

是否有一个优雅的解决方案可以让我将克隆对象上的所有内容设为只读?

注意: 看起来 lodash 有一个名为 cloneDeepWith 的方法需要一个 "customizer"... 想知道这是否可能朝着正确的方向发展。

关键是创建一个自定义类型 DeepReadonly<T>,您将使用它来代替 Readonly<>:

type DeepReadonly<T> = {
    readonly [K in keyof T]: DeepReadonly<T[K]>;
}

此类型将递归地将只读属性应用于所有嵌套对象。

See this in playground

type DeepReadonly<T> = {
    readonly [K in keyof T]: DeepReadonly<T[K]>;
}

import * as _ from 'lodash'; 

interface IParent{
   name: string;
}

interface IChild{
   name: string;
   parent: IParent;
}

class Child implements IChild{
   public name: string;
   public parent: IParent;

   constructor(props: IChild){
      Object.assign(this, props);
   }

   toImmutable(): DeepReadonly<IChild> {
       return _.cloneDeep(this); //lodash deep clone
   }
}

let child = new Child({ 
   name: 'abc',
   parent: { name: "123" }
});

let immutable = child.toImmutable();
immutable.name = 'abc';  //not allowed
immutable.parent.name = 'abc';  //not allowed