Reputation: 167
Say there's a function X
which takes any number of objects:
export function X<T extends object[]>(...classes: T): MergedClasses<T>;
I would like it to return the intersection of every object in that list.
For instance, given objects A
, B
, C
and D
it would go something like:
let X = A & B
X = X & C
X = X & D
// for any number of objects
However, I would like to do this as a type for the return type of the function (hence MergedClasses
)
type MergedClasses<C extends object[]> = {
}
I know it's possible to do [K for keyof C]:
but from there I'm not sure how I could combine the objects, since there is no way to assign variables.
I checked out Lodash's merge typings and it is just an overloaded function that takes 1, 2, 3, 4 or 5 objects. So it seems like it can merge any number of objects, but it can't actually do more than 5.
Is it possible to dynamically merge the objects, or will I have to take the same route as Lodash did?
Upvotes: 2
Views: 1902
Reputation: 51083
Here's a solution using a recursive definition:
type IntersectionOf<A extends any[]> = A extends [infer T, ...infer R] ? T & IntersectionOf<R> : unknown
// type Test = {a: string} & {b: number} & {c: boolean}
type Test = IntersectionOf<[{a: string}, {b: number}, {c: boolean}]>
Here's an alternative, slightly messier solution using the UnionToIntersection
helper from this answer, which might be preferable if the first solution runs up against the compiler's recursion limit. However, note that this will have incorrect behaviour if any of the input types is a union, since those will be converted to intersections too.
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type IntersectionOf<A extends any[]> = UnionToIntersection<A[number]>
// type Test = {a: string} & {b: number} & {c: boolean}
type Test = IntersectionOf<[{a: string}, {b: number}, {c: boolean}]>
Upvotes: 5