I want to create a wrapper function for an existing function in TypeScript.
The wrapper function could start some other process and clean it up after finishing the main ("callback") function passed to the wrapper.
This can be done using approaches like shown here. However, these solutions do not allow me to specify additional options that can be passed to the wrapper itself.
How would I go about doing that?
My starting point was:
export const wrap = async <T>(
callback: () => T | Promise<T>,
options?: { foo?: string | undefined },
): Promise<T> => {
let ret;
// begin
if (options.foo) {
// do something
}
try {
ret = await callback();
} catch (e) {
throw e;
} finally {
// cleanup
}
return ret;
};
This would not let me add arguments to callback()
. I can use ...args
, but how would I specify both ...args
and options
?
CodePudding user response:
The solution consists of making the functon generic (F
) and inferring the parameters and return types of that function via ReturnType
and Parameters
. Any additional options can be specified in advance of the destructured remaining arguments which are passed to the callback
function:
export const wrap = async <F extends (...args: any[]) => ReturnType<F> | Promise<ReturnType<F>>>(
callback: F,
options: { foo?: string | undefined } = {},
...args: Parameters<F>
): Promise<ReturnType<F>> => {
let ret: ReturnType<F> | undefined;
// begin
if (options.foo) {
// do something
}
try {
ret = await callback(...args);
} catch (e) {
throw e;
} finally {
// cleanup
}
return ret;
};
The wrapper can then be called like this:
const someFunction = (arg1: string, arg2: boolean): void => {
// do something
}
wrap(someFunction, { foo: "yes" }, arg1, arg2)`
All return types and argument types are inferred automatically.
CodePudding user response:
I suggest you to write your function as a factory (function that returns another function):
const wrap = <T>(
callback: (...args: any[]) => T | Promise<T>,
options?: { foo?: string | undefined },
): (...args: any[]) => Promise<T> => {
return async function (...args: any[]) {
let ret;
// begin
if (options && options.foo) {
// do something
}
try {
ret = await callback(...args);
} catch (e) {
throw e;
} finally {
// cleanup
}
return ret;
}
};
That allows you to create another function with the same signature as the callback, and possibly defer its execution.