我想编写一个递归地省略字段的类型实用程序。
您会这样命名和使用的东西 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'>
编辑:已更新以反射(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/