我想写一个类型实用程序,它递归地忽略字段。
您将使用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
答案 0 :(得分:3)
您不会递归调用OmitRecursevly
,而且我也只会在属性类型是对象的情况下递归地应用省略,否则它应该可以正常工作:
// Or more generic attempt
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
type OmitDistributive<T, K> = 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> = 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'>
注意 Id
主要是出于装饰性原因(它会迫使编译器在工具提示中扩展Pick
)并可以将其删除,有时可能会给某些核心人员带来问题案例。
修改
原始代码不适用于strictNullChecks
,因为属性的类型为type | undefined
。我编辑了代码以在工会上分发。条件类型OmitDistributive
用于其分布行为(出于这个原因,我们使用它而不是条件)。这意味着,OmitRecursively
将被应用到联合的每个成员。
说明
默认情况下,Omit
类型不适用于联合。 Omit
长相在联合作为一个整体,也不会提取从联盟中的每个成员的属性。这主要是由于keyof
仅返回联合的公共属性(因此,keyof undefined | { a: number }
实际上将是never
,因为没有公共属性)。
幸运的是,有一种方法可以使用条件类型深入到并集。有条件的类型将分布在裸类型参数(见here对我的解释或docs)。对于OmitDistributive
,我们并不真正在意条件(这就是为什么使用T extends any
的原因),我们只是关心如果我们使用条件类型T
会成为每个成员在工会中。
这意味着这些类型是等效的:
OmitDistributive<{ a: number, b: number} | undefined}, 'a'> =
OmitRecursively<{ a: number, b: number}, 'a'> | undefined
答案 1 :(得分:0)
当被Array包裹时,这不起作用。
type A = {
keyToKeep: string
keyToOmit: string
nested: {
keyToKeep: string
keyToOmit: string
}
nestedOptional?: {
keyToKeep: string
keyToOmit: string
}
foo: Array<{
keyToKeep: string
keyToOmit: string
}>
}