Typescript类型将嵌套字典扩展为单个字典
实际上,我已经创建了一个相当不错的方法来实现这一点,我认为它是有效的,直到现在,我又回到它,它似乎不再有效(只是显示为Typescript类型将嵌套字典扩展为单个字典,typescript,Typescript,实际上,我已经创建了一个相当不错的方法来实现这一点,我认为它是有效的,直到现在,我又回到它,它似乎不再有效(只是显示为从未-出于某种原因仍然在TS操场上有效)。 到目前为止,看起来是这样的: //https://github.com/microsoft/TypeScript/issues/13298#issuecomment-707364842 type UnionToArray<T> = ( ( ( T extends any
从未-出于某种原因仍然在TS操场上有效)。
到目前为止,看起来是这样的:
//https://github.com/microsoft/TypeScript/issues/13298#issuecomment-707364842
type UnionToArray<T> = (
(
(
T extends any
? (t: T) => T
: never
) extends infer U
? (U extends any
? (u: U) => any
: never
) extends (v: infer V) => any
? V
: never
: never
) extends (_: any) => infer W
? [...UnionToArray<Exclude<T, W>>, W]
: []
);
type IntersectObjectArray<A extends any> = A extends [infer T, ...infer R] ? T & IntersectObjectArray<R> : unknown
type ExpandTopKeys<A extends any> = A extends { [key: string]: infer X } ? { [K in keyof X]: X[K] } : unknown
type Expand<A extends any> = IntersectObjectArray<UnionToArray<ExpandTopKeys<A>>>;
type MergedClasses<C extends object[]> = Expand<IntersectObjectArray<C>>;
type MergedClasses<T extends readonly object[]> =
MergeProperties<MergeProperties<Omit<T, keyof any[]>>>
MergedClasses
将返回:
{
a: "green",
b: "blue",
c: "orange",
}
因此,它将对象合并,然后将“顶级”关键点扩展为单个对象
它目前所经历的步骤是:
- 将数组中的所有对象相交,即
[X,Y]
变为X&Y
- 展开“顶键”,即展开
foo
、bar
和extra
,将其转换为一个并集,如:
- 将联合转换为对象数组,即
[{a:“绿色”,b:“蓝色”},{c:“橙色”}]
- 最后,再一次,将所有这些物体相交在一起。
在做了所有这些之后,结果就是我想要的。然而,这感觉真的很不可靠,很容易破碎(老实说,似乎已经破碎了)
有没有一种更简单的方法可以合并任意数量的对象,并扩展它们的任何键?在TypeScript中并集到元组是一种可怕的脆弱类型操作,正常人甚至不应该考虑这样做。幸运的是,实现类型函数不必这样做
我在这里看到的基本操作是,您有一些对象类型(包括元组),其属性键应该被忽略(对于元组,这意味着“0”
和“1”
,等等),并且其属性值应该被合并。这种合并本质上是一种合并,尽管像{a:1,b:2,c:3}
这样的单个合并对象可能比像{a:1}&{b:2}&{c:3}
这样的可变交集更可取。让我们调用此操作MergeProperties
。所以MergeProperties
应该是{a:1,b:2}
,而MergeProperties
应该是{c:3,d:4}
然后,从概念上讲,您使用的MergedClasses
是MergeProperties
;您正在合并所有元组元素,然后合并结果的属性
下面是MergeProperties
的一个可能实现:
但不适用于元组:
type Oops = MergeProperties<[{ c: 1 }, { d: 2 }]>;
/* type Oops = never */
并验证它是否至少适用于您的示例:
type X = {
foo: {
a: "green",
},
bar: {
b: "blue",
}
}
type Y = {
extra: {
c: "orange",
},
}
type Z = MergedClasses<[X, Y]>
/* type Z = {
a: "green";
b: "blue";
c: "orange";
} */
类型X={
傅:{
答:“绿色”,
},
酒吧:{
b:“蓝色”,
}
}
Y型={
额外:{
c:“橙色”,
},
}
类型Z=合并类
/*类型Z={
a:“绿色”;
b:“蓝色”;
c:“橙色”;
} */
看起来不错。这里可能有很多边缘案例。可选属性、索引签名、联合等等,很可能会做一些奇怪的事情。您的原始版本可能有类似的问题,但它们可能差异很大,您应该确保在您的用例上测试它
我可能应该在帖子中提到这一点,但如果,例如,X
是{foo:{a:“red”}
,而Y
是{foo:{a:“green”}
的话,预期的输出应该是{a:“green”}
,而在上述解决方案中,它目前是永远不会。我也会给它一个镜头,但它可以通过删除任何重复的钥匙修复吗?也就是说,如果foo
存在于Y
中,则将其从X
中删除。请注意,您的原始版本也应该执行相同的永不
操作,因为它从元组元素相交开始。正如您所提到的,建议您在帖子中使用一个代表您所关心的边缘案例;否则,这种后续行动看起来像是范围爬行,而不是澄清。无论如何,考虑到你评论中的例子,我可能会重写它,这样我们就可以迭代元组元素,而不是不加区别地相交。我发现了另一个引起奇怪问题的次要边缘情况,但我成功地解决了它。这也让我了解了你的代码是如何工作的,这很有趣。你可以看到修复。谢谢你的帮助!
type Test = MergeProperties<{ x: { a: 1 }, y: { b: 2 } }>;
/* type Test = {
a: 1;
b: 2;
} */
type Oops = MergeProperties<[{ c: 1 }, { d: 2 }]>;
/* type Oops = never */
type MergedClasses<T extends readonly object[]> =
MergeProperties<MergeProperties<Omit<T, keyof any[]>>>
type X = {
foo: {
a: "green",
},
bar: {
b: "blue",
}
}
type Y = {
extra: {
c: "orange",
},
}
type Z = MergedClasses<[X, Y]>
/* type Z = {
a: "green";
b: "blue";
c: "orange";
} */