Reputation: 12005
TypeScript 3 brings a new feature as extension of tuple to use rest parameters with any type.
It looks like this:
function call<TS extends any[], R>(fn: (...args: TS) => R, ...args: TS): R {
return fn(...args);
}
function foo(x: number, y: string): string {
return (x + y).toLowerCase();
}
// The `TS` type parameter is inferred as `[number, string]`
let a = call(foo, 100, "Hello");
console.log(a);
What is main reason to use it, I can not understand advantage of this code
As I got this right, it allows to pass any parameters to function with any type.
Why dont use simple this, it gives the same result:
function call(fn, ...args) {
return fn(...args);
}
function foo(x: number, y: string, z: string): string {
return (x + y + z).toLowerCase();
}
// The `TS` type parameter is inferred as `[number, string]`
let a = call(foo, 100, "Hello", "sss");
console.log(a);
I think advantage in this:
function call<TS extends number[], R>(fn: (...args: TS) => R, ...args: TS): R {
return fn(...args);
}
function foo(x: number, y: number):number {
return x + y;
}
function foo2(x: number):number {
return x;
}
// The `TS` type parameter is inferred as `[number, string]`
let a = call((x: number, y: number) => x + y, 1, 2);
let b = call((x: number) => x, 1);
console.log(a);
console.log(b);
When TypeScript automaticly understands which function call depends of parameters.
Upvotes: 0
Views: 59
Reputation: 328004
See the TypeScript documentation and the relevant pull request introducing this feature for more information. Since 2015 there has been an open feature request in TypeScript, upvoted by hundreds of people, for the ability to manipulate unspecified-length tuples of types, also known as "variadic kinds".
The short answer for why this is desirable is quoted in many of those linked documents:
With these features it becomes possible to strongly type a number of higher-order functions that transform functions and their parameter lists.
In your example,
function call<TS extends any[], R>(fn: (...args: TS) => R, ...args: TS): R {
return fn(...args);
}
the call()
accepts a function and a list of arguments appropriate for that function. So this works:
call((x: number, y: number) => x + y, 1, 2);
But these don't:
call((x: number, y: number) => x + y, 1); // error
call((x: number, y: number) => x + y, 1, "whoops"); // error
Without tuple rest/spread, you could only strongly type that with a big list of overloads, which is tedious and fragile:
function call<R>(fn: () => R): R;
function call<T0, R>(fn: (t0: T0) => R, t0: T0): R;
function call<T0, T1, R>(fn: (t0: T0, t1: T1) => R, t0: T0, t1: T1): R;
function call<T0, T1, T2, R>(fn: (t0: T0, t1: T1, t2: T2) => R, t0: T0, t1: T1, t2: T2): R;
// ... more more more
function call(fn: Function, ...args: any[]): any {
return fn(...args);
}
Anyway, if you don't find it useful, don't use it. It will probably be most used in libraries, or other places where type manipulation is important.
Hope that helps. Good luck!
Upvotes: 2