如何递归地从类型中省略键

And*_*Los 4 recursion types typescript

我想编写一个递归省略字段的类型实用程序。你会这样命名和使用的东西OmitRecursively<SomeType, 'keyToOmit'>

我尝试使用映射类型 + 条件类型来完成它,但是当所有必需的字段都正确输入时我坚持了这种情况(因此字段从嵌套类型中消失了),但是该方法忽略了可选字段。

// This is for one function that removes recursively __typename field 
// that Appolo client adds
type Deapolify<T extends { __typename: string }> = Omit<
  { [P in keyof T]: T[P] extends { __typename: string } ? Deapolify<T[P]> : T[P] },
  '__typename'
>

// Or more generic attempt

type OmitRecursively<T extends any, K extends keyof T> = Omit<
  { [P in keyof T]: T[P] extends any ? Omit<T[P], K> : never },
  K
>
Run Code Online (Sandbox Code Playgroud)

预期行为将是 root,并且所有嵌套键的类型都应递归省略。例如

// This is for one function that removes recursively __typename field 
// that Appolo client adds
type Deapolify<T extends { __typename: string }> = Omit<
  { [P in keyof T]: T[P] extends { __typename: string } ? Deapolify<T[P]> : T[P] },
  '__typename'
>

// Or more generic attempt

type OmitRecursively<T extends any, K extends keyof T> = Omit<
  { [P in keyof T]: T[P] extends any ? Omit<T[P], K> : never },
  K
>
Run Code Online (Sandbox Code Playgroud)

Tit*_*mir 10

你不OmitRecursevly递归调用,如果属性类型是一个对象,我也只会递归应用省略,否则它应该主要工作:


type OmitDistributive<T, K extends PropertyKey> = T extends any ? (T extends object ? Id<OmitRecursively<T, K>> : T) : never;
type Id<T> = {} & { [P in keyof T] : T[P]} // Cosmetic use only makes the tooltips expad the type can be removed 
type OmitRecursively<T extends any, K extends PropertyKey> = Omit<
    { [P in keyof T]: OmitDistributive<T[P], K> },
    K
>

type A = {
    keyToKeep: string
    keyToOmit: string
    nested: {
        keyToKeep: string
        keyToOmit: string
    }
    nestedOptional?: {
        keyToKeep: string
        keyToOmit: string
    }
}

type Result = OmitRecursively<A, 'keyToOmit'>
Run Code Online (Sandbox Code Playgroud)

游乐场链接

编辑:更新以反映添加的内置Omit助手类型。对于旧版本,只需定义 Omit。

Note Id主要用于外观原因(它强制编译器Pick在工具提示中扩展)并且可以删除,它有时会在某些核心情况下导致问题。

编辑 原始代码不起作用,strictNullChecks因为属性的类型是type | undefined. 我编辑了代码以分发到工会。条件类型OmitDistributive用于其分配行为(我们出于这个原因而不是条件使用它)。这意味着OmitRecursively将应用于工会的每个成员。

说明

默认情况下,该Omit类型在联合上不能很好地工作。Omit将联合视为一个整体,不会从联合的每个成员中提取属性。这主要是因为它keyof只会返回联合的公共属性(keyof undefined | { a: number }实际上也是如此never,因为没有公共属性)。

幸运的是,有一种方法可以使用条件类型来钻取联合。条件类型将分布在裸类型参数上(请参阅此处了解我的解释或文档)。在OmitDistributive我们并不真正关心条件(这就是我们使用T extends any)的情况下,我们只关心如果我们使用条件类型T将依次成为联合中的每个成员。

这意味着这些类型是等效的:

OmitDistributive<{ a: number, b: number} | undefined}, 'a'> = 
     OmitRecursively<{ a: number, b: number}, 'a'> | undefined 
Run Code Online (Sandbox Code Playgroud)