I have a function whose only logic is to execute a function that is passed in.
const funcRunner = async (func: Function) => {
return await func()
}
I know that whatever the response of the function that is passed in will be the response of this same function.
However, this function return signature is inferred as Promise<any>
. Is there any way to keep the response type of the original function?
const bool = async isExample(): Promise<boolean> => return true;
// result should be boolean, not any
const result = await funcRunner(isExample);
CodePudding user response:
You can make it generic, so TypeScript can infer from the call site, and use the utility type ReturnType
to get its return type:
const funcRunner = async <FuncType extends () => any>(
func: FuncType
): Promise<ReturnType<FuncType>> => {
return await func();
};
Usage example:
async function example1() {
return 1;
}
async function example2() {
return "a";
}
(async() => {
const result1 = await funcRunner(example1);
// ^? const result1: number
const result2 = await funcRunner(example2);
// ^? const result2: string
})();
Just for fun: If you wanted to let funcRunner
call functions that expect parameters, you can add an args
argument that's fully typed:
async function funcRunner<FuncType extends (...args: any[]) => any>(
func: FuncType,
args: Parameters<FuncType>
): Promise<ReturnType<FuncType>> {
return await func(...args);
}
If you want to make that second argument optional for functions that don't take any parameters, I think you need to overload it:
async function funcRunner<FuncType extends () => any>(
func: FuncType,
): Promise<ReturnType<FuncType>>;
async function funcRunner<FuncType extends (...args: any[]) => any>(
func: FuncType,
args: Parameters<FuncType>
): Promise<ReturnType<FuncType>>;
async function funcRunner<FuncType extends (...args: any[]) => any>(
func: FuncType,
args?: Parameters<FuncType>
): Promise<ReturnType<FuncType>> {
if (args) {
return await func(...args);
}
return await func();
}
Usage examples for the overloaded one:
async function example1() {
return 1;
}
async function example2() {
return "a";
}
function example3() {
return Math.random() < 0.5;
}
function example4(num: number) {
return num * 42;
}
(async() => {
const result1 = await funcRunner(example1);
// ^?
const result2 = await funcRunner(example2, []); // <=== Args is allowed even when optional...
// ^?
const badResult1 = await funcRunner(example2, ["x"]); // <=== ...but it can't have anything in it
// ^?
const result3 = await funcRunner(example3);
// ^?
const result4 = await funcRunner(example4, [3]);
// ^?
const badResult2 = await funcRunner(example4); // <=== Error as desired, need args!
// ^?
const badResult3 = await funcRunner(example4, ["x"]); // <=== Error as desired, wrong type!
// ^?
})();
CodePudding user response:
You can define a generic type to run function:
type Runnable<T> = () => Promise<T>;
And then apply it for your funcRunner
async function funcRunner<T>(runnable: Runnable<T>): Promise<T> {
return await runnable();
}
Optionally you can use inline typing:
async function funcRunner<T>(runnable: () => Promise<T>): Promise<T> {
return await runnable();
}
Example usage:
async function example1(): Promise<number> {
return 1;
}
funcRunner(example1)
.then((result: number) => console.log(result))