Revision for Version 3.0
Although the initial answer was accurate, there have been changes in TypeScript since then. In typescript 3.0, it is now possible to utilize tuples in rest parameters to capture the type of arguments within a tuple.
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
function f<A extends any[]>(...args: A): UnionToIntersection<A[number]> { return null! }
var smth = f({ x: 1 }, new A(), new B()); // will be of type A & B & { x: number; }
Initial Response
While Proposal: Variadic Kinds could potentially aid in this situation as suggested by others, there are workarounds available for your specific scenario.
If we define the function signature with a single type argument, we can achieve a union type of the arguments:
function f<A>(...args: A[]): A {}
var smth = f({ x: 1 }, { y: 2 }, { z: 3 });
typeof smth = {
x: number;
y?: undefined;
z?: undefined;
} | {
y: number;
x?: undefined;
z?: undefined;
} | {
z: number;
x?: undefined;
y?: undefined;
}
An issue arises when using a class instead of an object literal as the compiler fails to infer the union and generates an error. By omitting the rest parameter (...
) and simply utilizing an array, the compiler deduces a union of the parameter type:
function f<A>(args: A[]): A { /*…*/}
var smth = f([{ x: 1 }, new A(), new B()]);
typeof smth == A | B | {
x: number;
}
To convert the union into an intersection, conditional types can be employed (refer to this for additional information)
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
function f<A>(args: A[]): UnionToIntersection<A> {
return Object.assign({}, ...args);
}
class A { z: number }
class B { y: number }
var smth = f([{ x: 1 }, new A(), new B()]); // will result in type A & B & { x: number; }
var res = smth.x + smth.y + smth.z;
We trust that this has provided a helpful workaround until the inclusion of variadic kinds.