Omid
Omid

Reputation: 448

how to fix a generic function that handles async functions

I want to create a generic function that accepts an async function, executes it and catches the error if the provided function encounters any problems.

since I come from a javascript background, I was able to create the following function that does just that. but I wanted to improve it using generics and failed to do so

this code works as expected. but i don't get any type on my 'msg' argument.

const botAsyncHandler = (fn:Function) => (msg: any) => {
  Promise.resolve(fn(msg)).catch(error => {
    console.log(error.message);
  });
};

so i tried to write the following

const botAsyncHandler = <T extends any>(fn:Function)=> (msg: T) => {
  Promise.resolve(fn(msg)).catch(error => {
    console.log(error.message);
    console.log('here');
  });
};

sadly my code does not work, and IDE still tells me that msg is of type any implicitly on the following code

bot.on('photo', botAsyncHandler(async msg => {}))

( returns : Parameter 'msg' implicitly has an 'any' type.)

but highlighting the botAsyncHandler shows the following:

botAsyncHandler<TelegramBot.Message>(fn: Function): (msg: TelegramBot.Message) => void

Which seems like what I want. I curious to know where I'm going wrong

Upvotes: 2

Views: 8299

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075249

From your example usage:

bot.on('photo', botAsyncHandler(async msg => {}))

and JavaScript example, it looks like you're trying to create a function that will return a function, which when called would call the original but handling errors from it. The simplest way to do that is just to write an async function with a try/catch in it. JavaScript version:

const botAsyncHandler = (fn) => async (msg) => {
  try {
    await fn(msg);
  } catch (error) {
    console.log(error.message);
  }
};

adding type annotations w/generics to it:

type AsyncHandler<T> = (arg: T) => {};
const botAsyncHandler = <T extends any>(fn: AsyncHandler<T>) => async (msg: T) => {
  try {
    await fn(msg);
  } catch (error) {
    console.log(error.message);
  }
};

That assumes the function must declare one formal parameter. Live on the playground.

But I'm not sure adding a type parameter buys you anything here, given you're using <T extends any>.

Upvotes: 1

Paleo
Paleo

Reputation: 23752

The Function type is useless. Do not use it. The type of the fn parameter is something like: (msg: T) => Promise<void>. Or you can decide to accept synchronous functions that returns void: (msg: T) => void | Promise<void>.

You could write:

const botAsyncHandler = <T>(fn: (msg: T) => void | Promise<void>) => async (msg: T) => {
    try {
        await fn(msg);
    } catch (err) {
        console.log(err.message);
    }
};

const onString = (msg: string) => {};
const fn = botAsyncHandler(onString);
// Here, the type of 'fn' is: '(msg: string) => Promise<void>'

And I can't test but your code should work as expected:

bot.on('photo', botAsyncHandler(async msg => {}))

Upvotes: 3

Related Questions