【问题标题】:Deep Omit with typescript使用打字稿进行深度省略
【发布时间】:2020-10-03 03:48:27
【问题描述】:

是否可以在一个函数上保持类型覆盖,从而深度删除对象中某个键的所有实例?

我的函数是这样的。

function omitDeep<T extends object>(obj: T, key: string): TWithoutProvidedKey {
  return JSON.parse(
    JSON.stringify(obj),
    (key: string, value: any) => key === "__typename" ? undefined : value
  );
}

有没有办法让TWithoutProvidedKey 成为现实?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这很容易做到,你只需要使用映射类型来递归属性:

    type Primitive = string | Function | number | boolean | Symbol | undefined | null 
    type DeepOmitHelper<T, K extends keyof T> = {
        [P in K]: //extra level of indirection needed to trigger homomorhic behavior 
            T[P] extends infer TP ? // distribute over unions
            TP extends Primitive ? TP : // leave primitives and functions alone
            TP extends any[] ? DeepOmitArray<TP, K> : // Array special handling
            DeepOmit<TP, K> 
            : never
    }
    type DeepOmit<T, K> = T extends Primitive ? T : DeepOmitHelper<T,Exclude<keyof T, K>> 
    
    type DeepOmitArray<T extends any[], K> = {
        [P in keyof T]: DeepOmit<T[P], K>
    }
    type Input =  {
        __typename: string,
        a: string,
        nested: {
            __typename: string,
            b: string
        }
        nestedArray: Array<{
            __typename: string,
            b: string
        }>
        nestedTuple: [{
            __typename: string,
            b: string
        }]
    }
    
    type InputWithoutKey = DeepOmit<Input, '__typename'>
    
    let s: InputWithoutKey = {
        a: "",
        nested: {
            b:""
        },
        nestedArray: [
            {b: ""}
        ],
        nestedTuple: [
            { b: ""},
        ]
    }
    

    请注意,这适用于 3.4,数组和元组上映射类型的处理最近发生了变化,因此根据版本,您可能需要将数组作为特殊情况处理。

    【讨论】:

    • 非常棒的解决方案!我遇到了一个问题,虽然看起来DeepOmit 将可选属性转换为必需属性。有什么办法可以防止这种情况发生吗?下面的代码 sn-p 抱怨 test 没有 world 属性 type Input = { __typename: string, hello: string world?: string } type InputWithoutKey = DeepOmit let test: InputWithoutKey = {你好:“你好”,}
    • 我发现的另一个问题是DeepOmit&lt;string, "__typename"&gt; 不等于string,因此如果您尝试将通过DeepOmit 运行的值传递给接受string 的函数,它将引发类型错误: type OmitString = DeepOmit let y: OmitString = "great" function cool(z: string) { return z } cool(y)
    • 扩展最后一条评论,这是处理字符串数组时出现的问题。这会导致类型错误,因为它将 DeepOmit 应用于类型字符串,这不等于类型字符串。 type Type1 = { __typename: string, arr: string[] } type TypeOmit = DeepOmit let x: TypeOmit = { arr: ["great", "breat"] } let z: string[] = x.arr
    • @TLadd 已编辑,修复了您的问题,如果您发现任何其他问题,请告诉我,很高兴看到该类型得到了很好的使用 :)
    • 可能只是我,但这不适用于 TS 3.5。
    【解决方案2】:

    这里的答案很鼓舞人心。我能够解决 TypeScript 4.0 的一些小问题。我把它作为一个要点:https://gist.github.com/ahuggins-nhs/826906a58e4c1e59306bc0792e7826d1。希望这对某些人有所帮助,尤其是那些想要在深度省略中处理 Partial 实用程序的人。

    /** Union of primitives to skip with deep omit utilities. */
    type Primitive = string | Function | number | boolean | Symbol | undefined | null
    
    /** Deeply omit members of an array of interface or array of type. */
    export type DeepOmitArray<T extends any[], K> = {
        [P in keyof T]: DeepOmit<T[P], K>
    }
    
    /** Deeply omit members of an interface or type. */
    export type DeepOmit<T, K> = T extends Primitive ? T : {
        [P in Exclude<keyof T, K>]: //extra level of indirection needed to trigger homomorhic behavior
            T[P] extends infer TP ? // distribute over unions
            TP extends Primitive ? TP : // leave primitives and functions alone
            TP extends any[] ? DeepOmitArray<TP, K> : // Array special handling
            DeepOmit<TP, K>
            : never
    }
    
    /** Deeply omit members of an array of interface or array of type, making all members optional. */
    export type PartialDeepOmitArray<T extends any[], K> = Partial<{
        [P in Partial<keyof T>]: Partial<PartialDeepOmit<T[P], K>>
    }>
    
    /** Deeply omit members of an interface or type, making all members optional. */
    export type PartialDeepOmit<T, K> = T extends Primitive ? T : Partial<{
        [P in Exclude<keyof T, K>]: //extra level of indirection needed to trigger homomorhic behavior
            T[P] extends infer TP ? // distribute over unions
            TP extends Primitive ? TP : // leave primitives and functions alone
            TP extends any[] ? PartialDeepOmitArray<TP, K> : // Array special handling
            Partial<PartialDeepOmit<TP, K>>
            : never
    }>
    

    【讨论】:

    • 你能提供一个简单的例子吗?
    • 谢谢我使用的是 typescript 4,我对第一个答案有点困惑!
    【解决方案3】:

    对于那些带着更高版本的 TS(我已经用 TS3.8.3 测试过)的人,你需要从 Titian 的答案中内联 DeepOmitHelper

    type Primitive =
      | string
      | Function
      | number
      | boolean
      | Symbol
      | undefined
      | null;
    
    type DeepOmitArray<T extends any[], K> = {
      [P in keyof T]: DeepOmit<T[P], K>;
    };
    
    export type DeepOmit<T, K> = T extends Primitive
      ? T
      : {
          [P in Exclude<keyof T, K>]: T[P] extends infer TP
            ? TP extends Primitive
              ? TP // leave primitives and functions alone
              : TP extends any[]
              ? DeepOmitArray<TP, K> // Array special handling
              : DeepOmit<TP, K>
            : never;
        };
    

    【讨论】:

    • 我正在尝试调整它以使深层属性成为可选(部分),而不是省略它,但我正在努力。
    猜你喜欢
    • 1970-01-01
    • 2022-11-22
    • 1970-01-01
    • 2015-07-23
    • 1970-01-01
    • 2019-10-19
    • 2022-12-01
    • 1970-01-01
    • 2020-02-14
    相关资源
    最近更新 更多