Jordan Baker
Jordan Baker

Reputation: 4625

How to type Generic Function Parameters for wrapping a callback style function into a more Promise style?

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

Answers (1)

Fyodor Yemelyanenko
Fyodor Yemelyanenko

Reputation: 11848

It is possible, but a bit tricky

  1. 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;
    
  2. Get type of last arg.

    type GetLastArgType<F extends ((...args: any[]) => any)> = Parameters<F>[GetLastArgNum<F>]
    
  3. 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>)

TS Playground

Upvotes: 1

Related Questions