Reputation: 4625
I'm trying to create a generic that handles translating a callback into a synchronous-style function call.
For example. f(err: Error, response: ResponseT):void
should become f(): ResponseT
How could I accomplish that?
I have a function called wrap() that already does that.. how do I declare it properly so it preserves types?
Trying something like
function wrapAsync<A extends any[], E, R>(f: (...args: A, err: E, response: R) => void): R
But that doesn't work clearly because REST args have to come after any others.
Upvotes: 1
Views: 221
Reputation: 11848
It is possible, but a bit tricky
Get number of last argument. Most trickiest part, as TS doesn't allow arithmetic for types. So we convert function to function with specific first argument. Then ...args
become array with length decreased by 1. Then we infer length of ...args
array.
type GetLastArgNum<F extends ((...args: any[]) => any)> =
F extends ((p1: any, ...args: infer R) => any) ?
R extends { length: infer L } ? L : never : never;
Get type of last arg.
type GetLastArgType<F extends ((...args: any[]) => any)> = Parameters<F>[GetLastArgNum<F>]
And finally convert to desired function type
type TransformFunction<F extends ((...args: any[]) => any)> = (() => GetLastArgType<F>)
And to apply to your task. If I understand correctly, you need function wrapAsync
which will accept callback function and return function which does not have any arguments but returns value of last argument type. It will look like
function wrapAsync<F extends ((...args: any[]) => any)> (f: F): (() => GetLastArgType<F>)
Upvotes: 1