Okay, so I couldn't get @Titian's answer to work; here's another solution
type Intersect<T extends any[]> = {[key in keyof T[number]] :T[number][key]};
And you can use it like this, say
See how the IDE knows its properties, and it error's if you try to assign anything else!
Unfortunately it thinks they are optional, you can combat this if you force them all to be real.
type Defined<T> = T extends (undefined|void) ? never : T;
type Intersect<T extends any[]> = {[key in keyof T[number]] :Defined<T[number][key]>};
The issue here is if you have a type that is {optional ?:string}
, I think you'll lose that nuance.
I thought that simple &
s would solve this issue, but it doesn't (and adds an upper bound to the size of your 'dynamic' array; in this case I could only be bothered copy-pasting for 6-length arrays).
type Intersect<T extends any[]> =
T[5] extends void ? T[4] extends void ? T[3] extends void ? T[2] extends void ? T[1] extends void ? T[0] extends void ?
[] : T[0] : T[0]&T[1] : T[0]&T[1]&T[2] : T[0]&T[1]&T[2]&T[3] : T[0]&T[1]&T[2]&T[3]&T[4] : T[0]&T[1]&T[2]&T[3]&T[4]&T[5]
;
I feel like the real solution will be when they figure out recursive types.
type Head<T> = T extends [infer U, ...Array<unknown>] ? U : never;
type Tail<T> = T extends any[] ?
(...args :T) => any extends (head :any, ...args :infer U) => any ?
U :
never
:
never
;
type Intersect<T extends any[]> = T[1] extends void ? T[0] : Head<T>&Intersect<Tail<T>>;
Head
& Tail
(which can pull the first type of an array, and the remaining types as a new array type respectively) both work, but Intersect
breaks when it refers back to Intersect
.