【问题标题】:Object.freeze / Object.seal in typescript打字稿中的 Object.freeze / Object.seal
【发布时间】:2018-02-05 19:15:05
【问题描述】:

Typescript 中是否建议使用 Object.freeze,或者是否有其他方法可以确保对象保持不可变?

由于const 只保护实例,而不保护属性,这显然不是我要寻找的答案。

【问题讨论】:

  • Object.freeze 是一个运行时概念。 TypeScript 只在 JavaScript 上运行,所以...
  • @DanielA.White,我自然是在谈论编译时检查,而不需要 Object.freeze 覆盖我的代码

标签: javascript typescript


【解决方案1】:

还有其他方法可以确保对象保持不变吗?

这取决于您想要购买的保险级别。

如果您想确保任何消费者(无论是 TypeScript 还是 JavaScript 代码)都不能在运行时修改对象属性,Object.freeze 就是这样做的方法。

如果编译时检查就足够了,例如,当所有消费者代码都保证是 TypeScript-only 并经过类型检查时,您可以使用 ReadOnly generic type,它采用对象类型并生成所有 properties readonly。事实上,Object.freeze()library type definitions 中被声明为返回ReadOnly - 其参数的修改类型:

freeze<T>(o: T): Readonly<T>;

对于seal,在类型系统中没有办法表示密封对象,Object.seal()就是declared to return the same type it receives

seal<T>(o: T): T;

Object.seal() 是唯一的出路。

【讨论】:

    【解决方案2】:

    最简单的解决方案:as const

    这代表了对所有级别的对象或数组的不变性的深度冻结。

    const a = {
        b: 1,
        c: {d: 'e'},
        f: ['g', 'h'],
    } as const
    

    这些都不允许覆盖

    a = 'not'
    a.b = 'going'
    a.c.d = 'to'
    a.f[1] = 'change'
    
    All result in error "TS2540: Cannot assign to '<name>' because it is a read-only property."
    

    注意:自定义对象可能不是完全不可变的,具体取决于您的实现。

    奖励:如果用作枚举替换,您可以添加此行

    type a = typeof a[keyof typeof a]
    

    不可变参数Immutable&lt;&gt;

    这与as const 的作用相同,可以深度冻结参数

    // Add this and make it reuseable
    type Immutable<T> = {
        readonly [K in keyof T]: Immutable<T[K]>
    }
    

    &lt;&gt;中定义类型,即Immutable&lt;MyType&gt;

    _method = (a: Immutable<{b:{c:{d:[string]}}}>) => {
        // This will complain
        a.b.c.d[0] = ""
    }
    

    Official Docs

    【讨论】:

      【解决方案3】:

      如果你想要运行时和编译时检查,你可以做,

      export const frozen:Readonly<{a:number}> = Object.freeze({a:1});
      

      然而,下面已经给了我一个 TypeError: Cannot assign to read only property

      export const frozen = Object.freeze({a:1});
      

      【讨论】:

        【解决方案4】:

        在对象和函数上递归 Object.freeze() 添加只读类型:

        export const deepFreeze = <T>(source: T, freezeParent = true): DRo<T> => {
          if (freezeParent) Object.freeze(source)
        
          Object.getOwnPropertyNames(source).forEach(function(prop) {
            if (
              Object.prototype.hasOwnProperty.call(source as any, prop) &&
              (source as any)[prop] !== null &&
              (typeof (source as any)[prop] === 'object' || typeof (source as any)[prop] === 'function')
            ) {
              if (Object.isFrozen((source as any)[prop])) {
                deepFreeze((source as any)[prop], false)
              } else {
                deepFreeze((source as any)[prop], true)
              }
            }
          })
        
          return source as DRo<T>
        }
        
        type DRo<T> = T extends (infer R)[] ? DRoArr<R> : T extends Function ? T : T extends object ? DRoObj<T> : T
        
        interface DRoArr<T> extends ReadonlyArray<DRo<T>> {}
        
        type DRoObj<T> = {
          readonly [P in keyof T]: DRo<T[P]>
        }
        

        Object.freeze 包括 Seal。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-12-11
          • 1970-01-01
          • 1970-01-01
          • 2023-03-07
          • 2012-09-23
          • 2021-07-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多