There's no way to do this in a 100% typesafe manner (AFAIK! Please point out if I missed something.)
In a perfect world, Typescript would support using a type param to stand in for an entire argument list. So you could do this:
function promiseMethod<T,R>(fn: (...args: T) => R) {
But that's not allowed. The best you can do is (...args: Array<any>), which is pretty lame.
(There is some discussion about this feature, see github issues here and here.)
You could take the sledgehammer approach and overload the promiseMethod function, like this:
function promiseMethod<R>(fn: () => R): () => Promise<R>; function promiseMethod<R,A>(fn: (a: A) => R): (a: A) => Promise<R>; function promiseMethod<R,A,B>(fn: (a: A, b: B) => R): (a: A, b: B) => Promise<R>; // etc... function promiseMethod<R>(fn: (...args: Array<any>) => R) { // implementation }
This might serve your needs but it does have a few issues:
- You'll still be able to call
promiseMethod with more args than your giant-est overload, and the typing in that situation will be leaky. - Also leaky if
fn itself has overloads (see below).
Still, it's better than ...args: Array<any>. A lot of commonly used libraries (e.g. lodash) use this pattern, for lack of any better alternative.
If the function you're passing in has overloads... good luck. Typescript's support for function overloads is pretty shallow (by design, it seems). If you refer to an overloaded function without calling it (e.g. by passing it into your promiseMethod function as a callback), it seems the compiler just uses the type signature of the last(?) defined overload, and throws away the others. Not very good. Of course, this will only bite you if you're actually passing in overloaded functions.
Finally, my opinion. Unless you are upgrading a large JS codebase to Typescript, I would consider avoiding the promisifying pattern entirely if possible. Especially with async/await now fully supported (since TS 2.1), I don't think there's a use for it.