【问题标题】:Typescript: Mapped type to make every property readonly except chosen properties打字稿:映射类型以使每个属性只读,除了选定的属性
【发布时间】:2020-06-01 06:20:02
【问题描述】:

我遇到了一个挑战,我不想继续重写多个接口。

我需要一个完全可写的接口,并且还需要该接口的“副本”,其中所有字段都是只读的,除了那些我选择为可写的字段。

Typescript 具有可能允许这样做的映射类型。

【问题讨论】:

    标签: typescript types mapped-types


    【解决方案1】:

    你可以使用 TypeScript 的Readonly utility type:

    type SomeType = {
      a: string;
    };
    
    const s: SomeType = {
      a: "can modify",
    };
    
    s.a = "new value";
    // ^ Can modify
    
    type ReadOnlyType = Readonly<SomeType>;
    
    const r: ReadOnlyType = {
      a: "cannot modify",
    };
    
    r.a = "new value";
    // ^ ERROR! - Cannot assign to 'a' because it is a read-only property.
    
    

    【讨论】:

      【解决方案2】:

      我们可以通过直接使用一些实用程序类型或映射类型来实现。考虑以下:

      type MakeSomePropsReadonly<A, K extends keyof A> = Readonly<Pick<A, K>> & Omit<A, K>
      
      type Original = {
          a: string,
          b: number,
          c: boolean
      }
      type Example = MakeSomePropsReadonly<Original, 'a'>
      const example: Example  = {
          a: 'a',
          b: 1,
          c: true
      }
      
      example.a = 'b'; // error a property is readonly
      example.b = 2; // ok it is not readonly
      

      我们的类型Readonly&lt;Pick&lt;A, K&gt;&gt; &amp; Omit&lt;A, K&gt; 所做的是:

      • Readonly&lt;Pick&lt;A, K&gt;&gt; - 创建一个只有 K 选择的键的类型,并且所有的都是只读的。结果我们只得到了只有我们选择的这些键的类型的一部分
      • &amp; Omit&lt;A, K&gt; 我们将先前的映射类型与仅只读属性的第二部分相交,因此Omit&lt;A, K&gt; 将创建对象,该对象将具有所有具有不同键的字段然后K

      因此,我们通过将一个具有所有只读属性的类型与另一个具有可写属性的类型连接起来来创建一个类型。


      我们也可以用另一种方式来做,所以我们有你描述的只读界面并选择哪些字段是可写的:

      type MakeSomePropsWritable<A, K extends keyof A> = {
          -readonly [Key in K]: A[Key]
      } & Omit<A, K>
      
      type Original = {
          readonly a: string,
          readonly b: number,
          readonly c: boolean
      }
      type Example = MakeSomePropsWritable<Original, 'a'>
      const example: Example  = {
          a: 'a',
          b: 1,
          c: true
      }
      
      example.a = 'b'; // this is now ok as we left it writable
      example.b = 2; // this is an error as expected
      

      在这个实现中没有什么不同,除了:

      • -readonly [Key in K]: A[Key] - 我们说我们通过 - 前缀从这些字段中删除只读

      【讨论】:

        【解决方案3】:
        export type DeepReadOnly<T> = { readonly [key in keyof T]: DeepReadOnly<T[key]> };
        export type DeepMutable<T> = { -readonly [key in keyof T]: DeepMutable<T[key]> };
        export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
        export type DeepKeepMutable<T, K extends keyof T> = DeepReadOnly<Omit<T, K>> & DeepMutable<Pick<T, K>>;
        
        // use as follows
        let o: DeepKeepMutable<Metadata, 'time' | 'payload'>;
        // this will keep time and payload writeable while the rest are readonly
        
        // it is also possible to extend and modify these types with index signatures, optional properties and level of depth
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-01-05
          • 2021-12-30
          • 2019-04-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-18
          相关资源
          最近更新 更多