To reach your objective, you have the option of passing arguments to your callback either in a single array value or sequentially using spread syntax.
Which syntax you prefer is up to you, but here's an interesting insight: when using the array syntax, the compiler treats the arguments as a tuple and retains label information, which doesn't seem to be the case with spread syntax (tested in TS v4.5.2
). It's possible that this behavior might change in a future release, as preserving label information can greatly enhance developer experience.
TS Playground link
type Fn<
Params extends unknown[] = any[],
Result = any,
> = (...params: Params) => Result;
// array args
function callerArr <
F extends Fn,
P extends Parameters<F>,
R extends ReturnType<F>,
>(fn: F, args: P): Fn<P, R> {
fn(...args);
return (...p: P) => fn(...p);
}
// spread args
function callerSpread <
F extends Fn,
P extends Parameters<F>,
R extends ReturnType<F>,
>(fn: F, ...args: P): Fn<P, R> {
fn(...args);
return (...p: P) => fn(...p);
}
const fn1 = (id: string) => {};
const fn2 = (key: string, value: string) => {};
// array args
callerArr(fn1, ['123']);
const recallArr = callerArr(fn2, ['1', '2']);
recallArr('1', '2');
// spread args
callerSpread(fn1, '123');
const recallSpread = callerSpread(fn2, '1', '2');
recallSpread('1', '2');