You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fixed params last in variable argument functions #1360
However, variable argument functions are limited to appearing only as the last argument and not the first argument. I propose support be added for having a variable argument appear first, followed by one or more fixed arguments:
functionsubscribe(...events: string[],callback: (message: string)=>void){}// the following would compilesubscribe(message=>alert(message));// gets all messagessubscribe('errorMessages',message=>alert(message));subscribe('errorMessages','customMessageTypeFoo123',(message: string)=>{alert(message);});// the following would not compilesubscribe();// supplied parameters do not match any signature of call targetsubscribe('a1');// argument of type 'string' does not match parameter of type '(message: string) => void'subscribe('a1','a2');// argument of type 'string' does not match parameter of type '(message: string) => void'
notes: it should be impossible for typescript code to call this function with zero arguments when typechecking. If JS or untyped TS code calls it without arguments, callback will be undefined. However, the same is true of fixed arguments at the beginning of the function.
edit: used a more realistic/motivating example for the fixed-last/variable-arguments-first function.
I'm assuming by fixed arguments you mean required arguments since this certainly couldn't work when mixed with other rest params or optional arguments. While this proposal would work I'm not sure I see this adding enough value. Are there a lot of existing JavaScript APIs that would require this sort of signature? If not, this isn't really adding any expressivity or much convenience, just reordering some arguments in a subset of signatures that use rest params. Offhand I can't think of any other languages with varargs that support using them in any position but the last.
Reacted by Nate Graf, Ryan van Zeben, csha, Dallas Reedy, Trotyl Yu, Tatsuki Saito, Frederic Barthelemy, ExE Boss, Franz Josef Kaiser and Rustam @SecondFry Gubaydullin
The problem here is index in the end being optional, but that is there problem, in an interface this still should work. Another example from rethinkdb client is the map function (http://rethinkdb.com/api/javascript/map/) which doesn't have the last field as optional.
For now I'm kind of "solving" it like Action and Func are defined in C# by simply creating new functions with more parameters:
but this isn't just a pain to do, it's also not the representation of the underlying javascript library
Reacted by Zheeeng, csha, Dallas Reedy, Frederic Barthelemy, Qwerty (Vítězslav Ackermann Ferko), ExE Boss, Franz Josef Kaiser, Siyuan Cao and Rustam @SecondFry Gubaydullin
I'm getting stumped by something. Trying to create the typings for Flyd; it's a library that creates streams. It has a function combine that combines a variable amount of streams in one. It works like this:
I've tried the method described by Peter Grman (@pgrm), but I'm getting Argument of type '(s: FlydStream, self: FlydStream) => void' is not assignable to parameter of type 'FlydCombinator', even if one of the overloaded calls matches this pattern exactly.
It seems the method works as long as I don't have another interface with overload that uses an overloaded interface. But the library is entirely made of curried functions calling each other, I just can't not have overloads using overloads.
I think I've managed to recreate a minimal test case here
Same here.
Tried to write type definitions for an library function which accepts a list of string arguments for filtering and as a last argument a callback. Only way to solve this right now I know is to prepare many copies of the function so it can be used in most cases.
Considering this issue was first opened in Dec 2014, I think this should definitely be added in the near future!
I wonder if allowing rest arguments in non-implementation signatures would be acceptable. That way we could potentially support this at the type level but we'd leave the runtime implementation to be decided by users.
Reacted by Jose Antonio Rodríguez, OD, sven, Aluan Haddad, Patrick Oswald, Georgii Dolzhykov, Niles Salter, Qwerty (Vítězslav Ackermann Ferko), Norton Wong and Joe Pea
lodash 4 uses it for a number of functions: _.differenceBy, _.differenceWith. _.xorBy, _.xorWith, _.intersectionBy, _.intersectionWith, _.unionBy, _.unionWith, _.zipWith, _.assignInWith, _.assignWith.
google api client library uses these types of callbacks, params where the last argument is a callback.
In general the args are usually fixed, but I want to easily convert any callback function in this style into a promise. To do so, it'd be nice to maintain type safety throughout.
Reacted by Frederic Barthelemy, Alexandra Petrova, Franz Josef Kaiser, Daniel M., Charlie Jonas and Rustam @SecondFry Gubaydullin
I was trying to make a function whose variable arguments are effectively a tuple of the same type, and I wanted to add an optional last parameter to allow a user to be able to optionally specify options on how to handle all the previous args (how to handle the tuple).
But remember to make someFunction set callback back to null so that the next call doesn't accidentally use the last-used callback. This forces the user to always specify callback before they call someFunction if they need a callback.
You will almost always want to follow the error callback convention, since most Node.js users will expect your project to follow them. The general idea is that the callback is the last parameter
I wanted this feature for typing a decorator that takes a function with a callback (node style) and add the promise api if no callback is provided. Thus, the function is now retro-compatible with the callback call and as a promise with async/await.
I wanted the types for the decorated function interface to be inferred from the original function.
Reacted by Christopher Mühl and Franz Josef Kaiser
While I agree that this feature is very important and long overdue, there is a good reason not to implement it: It is not compatible with the ecmascript implementation of the rest spread operator.
functionx(a, ...b,c){}
results in an error like: SyntaxError: Rest parameter must be last formal parameter.
I couldn't find a documented reason as to why this syntax is prohibited in ecmascript. But I believe it might warrant a request to the TC39 to add this functionality.
Reacted by Franz Josef Kaiser, Anton Anders, Chris Harvey, Chayim Refael Friedman, Sebastian Malton and jKey Lu
Activity
danquirk commentedon Dec 5, 2014
I'm assuming by fixed arguments you mean required arguments since this certainly couldn't work when mixed with other rest params or optional arguments. While this proposal would work I'm not sure I see this adding enough value. Are there a lot of existing JavaScript APIs that would require this sort of signature? If not, this isn't really adding any expressivity or much convenience, just reordering some arguments in a subset of signatures that use rest params. Offhand I can't think of any other languages with varargs that support using them in any position but the last.
RyanCavanaugh commentedon Dec 17, 2014
A cataloging of libraries that use this pattern would be useful.
noobiept commentedon Jan 1, 2015
Createjs uses this, for example:
http://createjs.com/Docs/EaselJS/classes/Container.html#method_addChildAt
http://createjs.com/Docs/EaselJS/files/easeljs_display_Container.js.html#l200
mhesler commentedon Apr 10, 2015
One example is String.prototype.replace. Although this occurs in the middle and not exclusively at the beginning or end.
lib.d.ts does this:
should be this:
pgrm commentedon May 5, 2015
I just came across this when updating TypeDefinitions for rethinkdb.
getAll(http://rethinkdb.com/api/javascript/get_all/) should look like this:The problem here is
indexin the end being optional, but that is there problem, in an interface this still should work. Another example from rethinkdb client is themapfunction (http://rethinkdb.com/api/javascript/map/) which doesn't have the last field as optional.For now I'm kind of "solving" it like
ActionandFuncare defined in C# by simply creating new functions with more parameters:but this isn't just a pain to do, it's also not the representation of the underlying javascript library
aholmes commentedon Jun 30, 2015
As mentioned in #3686, to add to the list of libraries doing this, lodash uses this pattern in extend/assign.
I also want to call out that lodash makes the
callbackandthisArgarguments optional.Xananax commentedon May 13, 2016
I'm getting stumped by something. Trying to create the typings for Flyd; it's a library that creates streams. It has a function
combinethat combines a variable amount of streams in one. It works like this:I've tried the method described by Peter Grman (@pgrm), but I'm getting
Argument of type '(s: FlydStream, self: FlydStream) => void' is not assignable to parameter of type 'FlydCombinator', even if one of the overloaded calls matches this pattern exactly.It seems the method works as long as I don't have another interface with overload that uses an overloaded interface. But the library is entirely made of curried functions calling each other, I just can't not have overloads using overloads.
I think I've managed to recreate a minimal test case here
olee commentedon Jun 5, 2016
Same here.
Tried to write type definitions for an library function which accepts a list of string arguments for filtering and as a last argument a callback. Only way to solve this right now I know is to prepare many copies of the function so it can be used in most cases.
Considering this issue was first opened in Dec 2014, I think this should definitely be added in the near future!
DanielRosenwasser commentedon Jul 8, 2016
It looks like
Ember.computeduses it.I wonder if allowing rest arguments in non-implementation signatures would be acceptable. That way we could potentially support this at the type level but we'd leave the runtime implementation to be decided by users.
thorn0 commentedon Oct 23, 2017
lodash 4 uses it for a number of functions:
_.differenceBy,_.differenceWith._.xorBy,_.xorWith,_.intersectionBy,_.intersectionWith,_.unionBy,_.unionWith,_.zipWith,_.assignInWith,_.assignWith.jsdevel commentedon Oct 12, 2018
koa-router also uses fixed params after varargs: https://www.npmjs.com/package/koa-router#named-routes
u8sand commentedon Dec 11, 2018
This has comes up a lot with certain style of function calling I've seen. The following is not possible as far as I'm aware, related to this issue.
google api client library uses these types of callbacks, params where the last argument is a callback.
In general the args are usually fixed, but I want to easily convert any callback function in this style into a promise. To do so, it'd be nice to maintain type safety throughout.
IlCallo commentedon May 31, 2019
Another use case: Jest
.eachfunction for data driven testing.DefinitelyTyped/DefinitelyTyped#34617
thierrymichel commentedon Jul 3, 2019
Some personal use case here passing a context as last argument: https://stackblitz.com/edit/ts-with-rest-params
BTW, if someone have a better approach… 😊
spion commentedon Sep 3, 2019
FastCheck uses a callback at the end of the parameter list to define a property:
https://cdn.jsdelivr.net/npm/fast-check@1.16.2/lib/types/check/property/Property.generated.d.ts
Reversing the argument order could help but its problematic to do that with existing APIs:
http://bit.ly/2k1qbKS
RecuencoJones commentedon Sep 17, 2019
Joi also uses this pattern:
trusktr commentedon Nov 30, 2019
I was trying to make a function whose variable arguments are effectively a tuple of the same type, and I wanted to add an optional last parameter to allow a user to be able to optionally specify options on how to handle all the previous args (how to handle the tuple).
F.e.
I think the easiest solution, as I have the ability to modify the signature of the API, is to allow it to be called as
or
This is easy to do. Here's a playground example.
trusktr commentedon Nov 30, 2019
Another way you can do it is to track a variable in the outer scope of your function. The call site would look like this:
or
But remember to make
someFunctionsetcallbackback tonullso that the next call doesn't accidentally use the last-used callback. This forces the user to always specifycallbackbefore they callsomeFunctionif they need a callback.Morikko commentedon Jan 7, 2020
From https://nodejs.org/en/knowledge/getting-started/control-flow/what-are-callbacks/
Module example:
Also there is
callbackifyandpromisifyin theutilmodule.https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_promisify_original
The types for
promisifyare incredibly long and not complete after too many parameters: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/util.promisify/index.d.tsI wanted this feature for typing a decorator that takes a function with a callback (node style) and add the promise api if no callback is provided. Thus, the function is now retro-compatible with the callback call and as a promise with async/await.
I wanted the types for the decorated function interface to be inferred from the original function.
martinheidegger commentedon Mar 25, 2020
While I agree that this feature is very important and long overdue, there is a good reason not to implement it: It is not compatible with the ecmascript implementation of the rest spread operator.
results in an error like:
SyntaxError: Rest parameter must be last formal parameter.I couldn't find a documented reason as to why this syntax is prohibited in ecmascript. But I believe it might warrant a request to the TC39 to add this functionality.
phaux commentedon Mar 25, 2020
But it still could be supported in function overloads and function types.
ChuckJonas commentedon Aug 6, 2020
it almost seems like this should be possible with the new Variadic Tuple Types releasing in 4.0?
Messed around with it a bit, but couldn't get it.
Attempt
Update
Go a response in this thread. As suspected, it can now be done!
Related question/Answer on SO
https://stackoverflow.com/questions/61273834/function-with-any-number-of-arguments-followed-by-a-callback-in-typescript/63295172#63295172
Andarist commentedon Aug 7, 2022
Ryan Cavanaugh (@RyanCavanaugh) Andrew Branch (@andrewbranch) this could be closed as it's currently possible with variadic tuple types.
RyanCavanaugh commentedon Aug 9, 2022
Thanks!