I've already actually created quite an unoptimised method to do this and I thought it was working until now that I have come back to it, it seems to no longer work (just shows as never
- still works on the TS playground for some reason).
So far, it looks like this:
//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>>;
And what it does is, given:
X = {
foo: {
a: "green",
},
bar: {
b: "blue",
}
}
Y = {
extra: {
c: "orange",
},
}
MergedClasses<[X, Y]>
will return:
{
a: "green",
b: "blue",
c: "orange",
}
So it takes the objects, combines them and then expands the "top level" keys into a single object.
The steps it currently goes through are:
- Intersect all objects in the array i.e.
[X, Y]
becomesX & Y
- Expand the "top keys" i.e. expand
foo
,bar
andextra
which converts it into a union like:
{
a: "green",
b: "blue",
} | {
c: "orange",
}
- Covert the union into an object array i.e.
[{ a: "green", b: "blue" }, { c: "orange" }]
- And finally, again, intersect all of those object together. After doing all of that, the result is what I want. However this feels really unrobust and easily breakable (and to be honest, seems to have already broken).
Is there a simpler way I could merge any number of objects, and expand any of their keys?