I want to pass arbitrary number of arguments of different types into a function and use these types. It looks like following:
function f<A>(a: A): A;
function f<A, B>(a: A, b: B): A & B;
function f<A, B, C>(a: A, b: B, c: C): A & B & C;
function f<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;
function f(...args: any[]) {
return Object.assign({}, ...args);
}
var smth = f({ x: 1 }, { y: 2 }, { z: 3 });
var res = smth.x + smth.y + smth.z;
As I want arbitrary number of parameters, I'd like to get rid of these declarations
function f<A>(a: A): A;
function f<A, B>(a: A, b: B): A & B;
function f<A, B, C>(a: A, b: B, c: C): A & B & C;
function f<A, B, C, D>(a: A, b: B, c: C, d: D): A & B & C & D;
and use a single declaration like:
function f<...T>(args: [...T]): &<...T>;
but this thing is syntactically wrong.
Is there a way to rewrite it in a correct way?
PS: Same question in Russian.
According to the TypeScript docs: void represents the return value of functions which don't return a value. Whenever you see a function returning void , you are explicitly told there is no return value. All functions with no return value have an inferred return type of void .
To return multiple values from a function in TypeScript, group the values in an array and return the array, e.g. return [myValue1, myValue2] as const . You can then destructure and use the values the function returns. Copied! We declared a function that returns multiple values by grouping them in an array.
A rest parameter allows us to pass zero or any number of arguments of the specified type to a function. In the function definition where we specify function parameters rest parameters should always come at last or the typescript compiler will raise errors.
To define the return type for the function, we have to use the ':' symbol just after the parameter of the function and before the body of the function in TypeScript. The function body's return value should match with the function return type; otherwise, we will have a compile-time error in our code.
Edit for 3.0
While the original answer is correct, since I first gave it typescript has changed. In typescript 3.0 it is possible to use tuples in rest parameters to capture the type of the arguments in 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 have type A & B & { x: number; }
Original answer
While as others have mentioned Proposal: Variadic Kinds would help with this task, we can find some workarounds for your specific example.
If we write the function signature with a single type argument, we can get 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;
}
The problem with this approach is that if we use a class instead of an object literal the compiler will refuse to infer the union and give us an error instead. If we let the rest parameter go (...
) and just use an array the compiler will infer 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;
}
So now we have a union of types, but you want an intersection. We can convert the union to an intersection using conditional types (see this answer)
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 have type A & B & { x: number; }
var res = smth.x + smth.y + smth.z;
Hope this helps, and gives you a usable workaround at least until we get variadic kinds.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With