While rest parameters need to be the last in JavaScript, this limitation doesn't have to apply to the type system in TypeScript. I would love to see the ability for rest types to appear anywhere in a tuple type, e.g. like this:
exportfunctioncallbackify(fn: ()=>Promise<void>): (callback: (err: NodeJS.ErrnoException)=>void)=>void;exportfunctioncallbackify<TResult>(fn: ()=>Promise<TResult>): (callback: (err: NodeJS.ErrnoException,result: TResult)=>void)=>void;exportfunctioncallbackify<T1>(fn: (arg1: T1)=>Promise<void>): (arg1: T1,callback: (err: NodeJS.ErrnoException)=>void)=>void;exportfunctioncallbackify<T1,TResult>(fn: (arg1: T1)=>Promise<TResult>): (arg1: T1,callback: (err: NodeJS.ErrnoException,result: TResult)=>void)=>void;exportfunctioncallbackify<T1,T2>(fn: (arg1: T1,arg2: T2)=>Promise<void>): (arg1: T1,arg2: T2,callback: (err: NodeJS.ErrnoException)=>void)=>void;exportfunctioncallbackify<T1,T2,TResult>(fn: (arg1: T1,arg2: T2)=>Promise<TResult>): (arg1: T1,arg2: T2,callback: (err: NodeJS.ErrnoException,result: TResult)=>void)=>void;exportfunctioncallbackify<T1,T2,T3>(fn: (arg1: T1,arg2: T2,arg3: T3)=>Promise<void>): (arg1: T1,arg2: T2,arg3: T3,callback: (err: NodeJS.ErrnoException)=>void)=>void;exportfunctioncallbackify<T1,T2,T3,TResult>(fn: (arg1: T1,arg2: T2,arg3: T3)=>Promise<TResult>): (arg1: T1,arg2: T2,arg3: T3,callback: (err: NodeJS.ErrnoException,result: TResult)=>void)=>void;exportfunctioncallbackify<T1,T2,T3,T4>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4)=>Promise<void>): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,callback: (err: NodeJS.ErrnoException)=>void)=>void;exportfunctioncallbackify<T1,T2,T3,T4,TResult>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4)=>Promise<TResult>): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,callback: (err: NodeJS.ErrnoException,result: TResult)=>void)=>void;exportfunctioncallbackify<T1,T2,T3,T4,T5>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5)=>Promise<void>): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,callback: (err: NodeJS.ErrnoException)=>void)=>void;exportfunctioncallbackify<T1,T2,T3,T4,T5,TResult>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5)=>Promise<TResult>): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,callback: (err: NodeJS.ErrnoException,result: TResult)=>void)=>void;exportfunctioncallbackify<T1,T2,T3,T4,T5,T6>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,arg6: T6)=>Promise<void>): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,arg6: T6,callback: (err: NodeJS.ErrnoException)=>void)=>void;exportfunctioncallbackify<T1,T2,T3,T4,T5,T6,TResult>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,arg6: T6)=>Promise<TResult>): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,arg6: T6,callback: (err: NodeJS.ErrnoException,result: TResult)=>void)=>void;exportfunctionpromisify<TCustomextendsFunction>(fn: CustomPromisify<TCustom>): TCustom;exportfunctionpromisify<TResult>(fn: (callback: (err: Error|null,result: TResult)=>void)=>void): ()=>Promise<TResult>;exportfunctionpromisify(fn: (callback: (err: Error|null)=>void)=>void): ()=>Promise<void>;exportfunctionpromisify<T1,TResult>(fn: (arg1: T1,callback: (err: Error|null,result: TResult)=>void)=>void): (arg1: T1)=>Promise<TResult>;exportfunctionpromisify<T1>(fn: (arg1: T1,callback: (err: Error|null)=>void)=>void): (arg1: T1)=>Promise<void>;exportfunctionpromisify<T1,T2,TResult>(fn: (arg1: T1,arg2: T2,callback: (err: Error|null,result: TResult)=>void)=>void): (arg1: T1,arg2: T2)=>Promise<TResult>;exportfunctionpromisify<T1,T2>(fn: (arg1: T1,arg2: T2,callback: (err: Error|null)=>void)=>void): (arg1: T1,arg2: T2)=>Promise<void>;exportfunctionpromisify<T1,T2,T3,TResult>(fn: (arg1: T1,arg2: T2,arg3: T3,callback: (err: Error|null,result: TResult)=>void)=>void): (arg1: T1,arg2: T2,arg3: T3)=>Promise<TResult>;exportfunctionpromisify<T1,T2,T3>(fn: (arg1: T1,arg2: T2,arg3: T3,callback: (err: Error|null)=>void)=>void): (arg1: T1,arg2: T2,arg3: T3)=>Promise<void>;exportfunctionpromisify<T1,T2,T3,T4,TResult>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,callback: (err: Error|null,result: TResult)=>void)=>void): (arg1: T1,arg2: T2,arg3: T3,arg4: T4)=>Promise<TResult>;exportfunctionpromisify<T1,T2,T3,T4>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,callback: (err: Error|null)=>void)=>void): (arg1: T1,arg2: T2,arg3: T3,arg4: T4)=>Promise<void>;exportfunctionpromisify<T1,T2,T3,T4,T5,TResult>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,callback: (err: Error|null,result: TResult)=>void)=>void): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5)=>Promise<TResult>;exportfunctionpromisify<T1,T2,T3,T4,T5>(fn: (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5,callback: (err: Error|null)=>void)=>void): (arg1: T1,arg2: T2,arg3: T3,arg4: T4,arg5: T5)=>Promise<void>;exportfunctionpromisify(fn: Function): Function;
Search Terms
rest element type tuple last
Related:
#24897
Suggestion
As of #24897, we can now use rest types in tuples to type argument lists. This is very nice since it allows us to easily type a couple of common methods. However there's still one big offender, which are the NodeJS callback style API methods. These generally accept a couple of arguments, with the callback method being the last one. Borrowing from the example in the RC-3.0 docs:
While rest parameters need to be the last in JavaScript, this limitation doesn't have to apply to the type system in TypeScript. I would love to see the ability for rest types to appear anywhere in a tuple type, e.g. like this:
Use Cases
Typing the
promisifyandcallbackifymethods and similar patterns. Currently they look like this monster, which is inherently incomplete:https://github.com/DefinitelyTyped/DefinitelyTyped/blob/91f8a6b7d1ef61cc543d0901dcc004b846283345/types/node/index.d.ts#L6275
Examples
With this functionality in place, we could type the above methods like this:
Checklist
My suggestion meets these guidelines: