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

标签 typescript recursion types

我想编写一个递归地省略字段的类型实用程序。 您会这​​样命名和使用的东西 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
>

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

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

type Result = OmitRecursively<A, 'keyToOmit'>

type Expected = {
  keyToKeep: string
  nested: {
    keyToKeep: string
  }
  nestedOptional?: {
    keyToKeep: string
  }
} 

Expected === Result

最佳答案

您不会递归地调用 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'>

Playground link

编辑:已更新以反射(reflect)内置Omit 助手类型的添加。对于旧版本,只需定义省略。

注意 Id 主要用于美观原因(它强制编译器在工具提示中展开 Pick)并且可以删除,有时在某些核心案例中可能会导致问题。

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

解释

默认情况下,Omit 类型在联合上效果不佳。 Omit 将联合视为一个整体,不会从联合的每个成员中提取属性。这主要是因为 keyof 只会返回联合的公共(public)属性(所以 keyof undefined | { a: number } 实际上是 never,因为没有公共(public)属性)。

幸运的是,有一种方法可以使用条件类型来钻取联合。条件类型将分布在裸类型参数上(参见 here 我的解释或 docs )。在 OmitDistributive 的情况下,我们并不真正关心条件(这就是我们使用 T extends any 的原因)我们只关心我们是否使用条件类型 T 依次是联合体中的每个成员。

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

OmitDistributive<{ a: number, b: number} | undefined}, 'a'> = 
     OmitRecursively<{ a: number, b: number}, 'a'> | undefined 

关于typescript - 如何递归地从类型中省略键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54487137/

相关文章:

javascript - 从多个数组之一更新元素

angular - 在不共享同一父组件的子组件之间共享 Angular 2 服务

java - 递归方法上的 StackOverflowError

java - 如何测试一个集合是否包含该集合中其他两个数字的和

node.js - 如何将顶级异步等待与 typescript 一起使用?

Typescript 模块扩充与多个文件中的模块

java - 与选择( float )混淆

java - 如何在我的泛型方法中限制只接受一种类型?

java - 如何在没有局部变量的情况下一次一个字符地反向打印字符串

javascript - 如何正确检测变量类型并检测比 typeof 函数提供的更多类型?