TypeScript won't infer this higher-order type operation for you; you'll have to annotate and assert in some places. Inspecting the type of wrapper shows
/* function wrapper<P extends any[]>(fn: (...params: P) => any): (...params: P) => void */
so it returns a function whose parameters are the same as the input function, but which returns void. In this case I'd write wrap() like:
function wrap<T extends Record<keyof T, (...args: any) => any>>(functions: T) {
const wrapped = {} as { [K in keyof T]: (...p: Parameters<T[K]>) => void };
for (const key in functions) {
wrapped[key] = wrapper(functions[key]);
}
return wrapped;
}
The functions argument is of generic type T which is constrained to an object type whose properties are functions. And the return wrapped is asserted to be of a mapped type which has the same property names as T, and whose value function types are the same as the corresponding property of T but where the return type has been changed to void. This uses the Parameters utility type to say that the output functions take the same parameters as the input functions.
I like to have complete examples when I write code for Stack Overflow, so here's the definitions I'm using:
const functions = {
foo: (x: string) => x.length,
bar: (x: number) => x.toFixed(2),
baz: (x: boolean, y: boolean) => x && y;
}
const doSomething = (result: any) => { console.log(result) };
And then when I call wrap() we get the following behavior:
const wrapped = wrap(functions);
wrapped.foo("hey"); // 3
wrapped.bar(Math.PI); // 3.14
wrapped.baz(true, false); // false
If I try something wrong, the compiler complains:
wrapped.foo(true); // error! boolean is not a string
wrapped.foo("string", false); // error! expected 1 argument, got 2
Looks like what you want, I think.
Playground link to code