DreamingInsanity
DreamingInsanity

Reputation: 167

Typescript generics loop over object array and intersect them

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

Answers (1)

kaya3
kaya3

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}]>

Playground Link

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}]>

Playground Link

Upvotes: 5

Related Questions