How to maintain optional arguments in mapped typescript function signature

Viewed 77

I'm trying to author a typesafe version of the classic promisify function. See my playground link here.

Node.js style callbacks generally have the form of (value?: ValueType, err?: ErrorType). Of course, they can also have an inner variadic form, such as (a?: A, b?: B, ..., n?: N, err?: Error). Covering the first form is easy. Covering the latter, I've found, is difficult. Why?

Well, I'm finding that type inference doesn't work well with optional arguments.

  1. First, to get the parameters of the node callback, I used Parameters<MyCallbackType>,
  2. Second, I try and get all inferred callback params up to last param, omitting the the err?: Error arg. I'm calling the slice of the array that includes everything but the tail Body, and defining it as: export type Body<T> = T extends [...infer B, infer _TailEl] ? B : never;

But... Body<T> doesn't work great when there are optional items in the tuple type. The problemo is that arg n?: N and err?: Error from Parameters do not appear to be being partitioned/considered in the infer, thus my conditional type lookup fails, and I'm out-of-luck.

My hack has been to Required wrap Parameters<...>, of which then I can successfully get the Body of the callback, and nicely reflect back a clean callback signature to the return value of promisify(fn)(args) => Promise<WellTypedTupleArgsFromCallback>.

One big exception--WellTypedTupleArgsFromCallback is now a tuple where everything is guaranteed because of the Required, which is a dishonest type.

This is very easy to see at work in the attached playground, and has some tests to show what is correct/incorrect.

How can I correctly map the variadic callback Body with optional elements?

1 Answers

Lucky for me, I made an error in my assumptions. Node callback style is (err, ...values), not the inverse. It's been so long since using node callback style, that I forgot the correct signature ;).

This correct arg layout forces typing on the err as null/undefined/Error/unknown from user space, and removes the lack of discrimination on the optional args.

updated playground with corrected solution and nomenclature

Related