You can achieve your goal by providing the arguments to your callback either as a single array value, or serially using spread syntax.
You can decide which syntax fits your preference, but here's an observation: by using the array syntax, the compiler treats the args as a tuple and preserves label information, but doesn't seem to do so using spread syntax (tested in TS v4.5.2). Perhaps this will change in a future release, as the label information is great for 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');