我的目标是,对于包含
Nested
泛型的给定类型
type Example = { a: string, b: string, n: Nested<{ n1: 'n1-val', n2: 'n2-val' }> }
通过实现 Nested
:将
FlatNested
扁平化为父类型
type t1 = FlatNested<Example>
// ^? goal: {a: string;b: string;n1: "n1-val" ;n2: "n2-val";}
目前进展type Nested<T> = T &
{ _: string } // this just so Nested<T> wont be equal to T
type Example = { a: string, b: string, n: Nested<{ n1: 'n1-val', n2: 'n2-val' }> }
type FlatNested<T> = { [K in keyof T as T[K] extends Nested<infer N> ? keyof N : K]: T[K] extends Nested<infer N> ? T[K][keyof N] : T[K] };
type t1 = FlatNested<Example>
// ^? type t1 = {a: string;b: string;n1: "n1-val" | "n2-val";n2: "n1-val" | "n2-val";}
// ^? goal: {a: string;b: string;n1: "n1-val" ;n2: "n2-val";}
我已经很接近了,但还没有实现,我无法分离嵌套类型中键的值。无论我尝试什么,我最终都会得到所有值的并集,而不是与当前重命名的映射类型匹配的值(as
之后的内容)。
键重新映射 来执行此操作,因为每个键 K
只能在输出中产生单个属性类型,即使您将
K
映射到键的union 也是如此。本质上,当您将单个键映射到多个键时,您不会得到新的“循环”。 相反,我倾向于将
T
的每个属性转换为我们相交以形成整体的某个部分。 我们希望
FlatNested<{ a: string, b: string, n: Nested<{ n1: 'n1-val', n2: 'n2-val' }> }>
成为类似
{a: string} & {b: string} & {n1: 'n1-val', n2: 'n2-val'}
的东西。然后可以将该交叉点展平为您正在寻找的类型。为此,我将执行类似
将并集类型转换为交集类型中所示的技术,其中我们将预期的片段移动到逆变类型位置(参见方差、协方差、逆变、双方差和不变性之间的差异)在 TypeScript 中)并从中推断以获得交集:
type FlatNested<T> = {
[K in keyof T]: (x: T[K] extends Nested<infer N> ? N : Pick<T, K>) => void
} extends { [k: string]: (x: infer I) => void } ? { [K in keyof I]: I[K] } : never
对于每个属性T[K]
,如果它是一个
Nested<infer N>
,那么我们想要相交的部分就是
N
。否则我们想要与
Pick<T, K>
相交,这只是当前属性。因此,如果
K
是
"a"
并且
T[K]
是
string
,那么我们要使用
{a: string}
,即
Pick<T, "a">
。这被映射到逆变类型位置(函数的参数),然后我们从中推断出单个函数参数类型,这给了我们交集作为
I
。最后我们用一个简单的身份
映射类型将其展平(请参阅如何查看 Typescript 类型的完整扩展契约?)。
type T1 = FlatNested<Example>
/* type T1 = {
a: string;
b: string;
n1: 'n1-val';
n2: 'n2-val';
} */
看起来不错。