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";
} */