2

Closed

Allow additional parameters after ..rest in declarations

description

I noticed we cannot express certain variable signatures in declarations:

In asynchronous javascript the callback always comes as the final parameter but I cannot declare this if the total number of arguments is variable. Because to get the variable arguments I need to use the ..rest argument and it cannot be followed by another more strictly defined parameter.

What I want is something like this:
fn(...values:any[], callback:AsyncCallback):void;
Also with optional callback:
fn(...values:any[], callback?:AsyncCallback):void;
While currently I have to settle for
fn(...args:any[]):void;
My first use case is the waterfall() method in async.js: their api allows to pass a variable amount of arguments to the next function in the chain but with the final argument always being the callback. I want to declare this in updated DefinitelyTyped declarations but it's impossible to model now so I have to make them any[] and hope people remember the callback.

For actual TypeScript code it'll work with additional code generation but could be undesirable.

I think this should work in declarations at least.
Closed Jul 28, 2014 at 10:18 PM by jonturner
As part of our move to GitHub, we're closing our CodePlex suggestions and asking that people move them to the GitHub issue tracker for further discussion. Some feature requests may already be active on GitHub, so please make sure to look for an existing issue before filing a new one.

You can find our GitHub issue tracker here:
https://github.com/microsoft/typeScript/issues

comments

paulb wrote Jun 7, 2013 at 5:13 PM

This is by design, there is no way to separate the last variable from the rest args..

The best I can come up with is to have the user create the array of arguments passed to the next function.

** Closed by paulb 06/07/2013 10:13AM

Bartvds wrote Jun 7, 2013 at 10:48 PM

Why is that not possible for the definition?

If the final argument is non-optional it's always the last one (which can then be checked for validity).

For example:
fn(...values:any[], callback:AsyncCallback):void;

fn(1,2,3, (value) => {
  ..
})

fn((value) => {
  ..
})

//error
fn(1, 2, 3);
//error
fn();

Bartvds wrote Jun 8, 2013 at 9:03 AM

FYI: the lib I try to re-model currently: https://github.com/caolan/async.

With 5000 github stars it's a prominent package and it depends heavy on the async-callback-last convention. The methods waterfall() and applyEach() both use the optional arguments length with the callback last.

So in plain javascript this apparently works fine: I'm not clear why this is impossible in TypeScript if the parameters after the ...rest are required.

If it's problematic in the generated code then please do allow it in declarations since otherwise we cannot fully re-model one of the main patterns in modern JS coding.

Bartvds wrote Mar 27, 2014 at 8:49 PM

I have another real-world case for this so I re-open:

I'm writing a type definition for XRegExp and it has a method (XRegExp.replace) that you call with a parameter that is a function with this description:
Replacement functions are invoked with three or more arguments:
  • The matched substring (corresponds to $& above). Named backreferences are accessible as properties of this first argument.
  • 0..n arguments, one for each backreference (corresponding to $1, $2, etc. above).
  • The zero-based index of the match within the total search string.
  • The total string being searched.
So this should be legal:
(match: string, ...refs: string[], index: number, input: string) => string;
A requirement could be that it is not allowed to have other optional parameters. The rest can be verified reliably (as there is a hard minimum and a known place where it resizes).

jamesnw wrote Mar 27, 2014 at 9:53 PM

I recently needed this also, and will have to resort to simply informing my API end users that the last argument is always a reference to a special object instance. I would love to allow users to specify a ", obj?: Type)" at the end in case they wanted it for their callback (I have my own special reasons for this, one of which being that I'm using genetics to pull call signature details for callbacks that usually don't care about the sender).

RyanCavanaugh wrote Mar 31, 2014 at 6:03 PM

Possible in principle. If you're using a library that makes extensive use of this, please upvote this issue and leave a comment with the API so we can get an idea of how widespread this pattern is.

Bartvds wrote Mar 31, 2014 at 6:48 PM

Sure. Keep in mind many of these cases now have Function as type or a signature using ..any[], as the type as definition authors who have encountered this have given up on it; so it might take a while before we gather some more votes.

Anyway, there are the asyc.waterfall and the XRegExp.replace I mentioned earlier.

There is one in lib.d.ts too: the callback in String.prototype.replace https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter

Is now:
replace(searchValue: RegExp, replaceValue: (substring: string, ...args: any[]) => string): string;
Should be:
replace (searchValue: RegExp, replaceValue: (match: string, ...refs: string[], index: number, input: string) => string): string;