Home > Blockchain >  Return typings for dynamic functions
Return typings for dynamic functions

Time:01-30

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
})();

Playground link


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!
    //    ^?
})();

Playground link

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))

  • Related