-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature Request: Tuples with variable type arguments #1336
Comments
Looks sensible. The obvious caution is how is this feature overlaps with varargs, when the variable-size argument is the one typed in this var-tuple way. |
Good point. I think that works out syntactically - this new syntax is bounded within the tuple annotation: function foo(...injectables: [...string, (...args: any) => any]) {} The issue is likely to be the inability to implement the same flexibility with function varargs as you can with variable tuple type arguments, because the function varargs are referencable individually: // this we can't do, but we could
function example1(...arg1: string, arg2: number, arg3: string) {
}
// translation to JS:
function example1() {
var arg1 = [];
var arg2 = arguments[arguments.length - 2];
var arg3 = arguments[arguments.length - 1];
for(var _i = 0; _i < arguments.length - 2; _i++) {
arg1[_i] = arguments[_i];
}
}
// this we can do now
function example2(arg1: string, ...arg2: string) {}
// this we cannot do.
function example3(...arg1: string, ...arg2: number) {
// what does the expression arg2[n] translate to? arg2.length? no way to tell at runtime
} There might be a workaround if you could somehow specify varargs as a tuple instead of splats, but this could get confusing. Or you could just live with tuples being more flexible than varargs. Or, you could extend varargs to enable example1 above, and not allow foo3, foo4, or foo5 above, so that they're consistent. Edit: updated example1 to match what the compiler currently does for varargs, slightly modified to support varargs at the beginning and fixed args at the end. |
I have use case where array is typed differently for different indexes. I would like to have this sample. class A {
a: [x: number, ...rest: { prop: number; }] = [1, {prop:2}, {prop:3}];
} |
Leaning toward "too complex" here unless there are more compelling examples than Angular's wacky DI setup. Might be worth discussing if we had a proof-of-concept PR that didn't introduce too much complexity. |
@RyanCavanaugh what about the unlimited number of arguments passed to |
This would be useful when e.g. defining a table row like this: const rows: [string, number, ..rest: any][] = []
const ID = 123;
rows.push(["My data", ID, prop1, prop2, prop3, prop4]); |
@saschanaz The following is similar to parameter packing in C++ module Promise {
function all<...T_values>(
values: [ (<T|Thenable<T>> T_values...) ] // unpack, cast, then repack to tuple ?
): Promise<T_values> // keep it packed, assuming the type is a tuple of whatever types
} Maybe now you see @RyanCavanaugh's point, about how complicated this can get. What other transformations will need to be supported? Say I want to implement How deep is the rabbit hole? |
@jameskeane You may want to continue discussion here: #5453 |
@RyanCavanaugh Most of the RxJS API is similarly typed. See for an example the concat operator. Notice the scheduler argument they have to pass at the end. They ended up having to write their own tooling for regenerating all the overloaded type definitions in their source files every time they make a change, and it still isn't properly statically typed (they're forced to cop out and use a union of the observable types and the scheduler as the return type, which means discipline and understanding of the API is required on the part of the library consumer). Unfortunately most JS libraries out there don't really care about the wackiness of this pattern in a statically typed world. The brunt is borne by developers of TypeScript definitions, who usually don't have a full team of contributors available to write their own tooling for generating lots of overloads. A typical developer writing typings just wants to crank something out in 10 minutes and be using their JS library. If things remain this painful on this front, it affects everyone consuming community typings. I came across this issue as a result of @unional asking on the typings gitter how he would represent the following function signature:
The proposal above would have allowed him to represent this type signature very conveniently. The current state of affairs requires him to write lots of overloads, or sacrifice some type safety and use the misleading |
@jayphelps Thanks, that is informative. FWIW, the proposal here is only syntactically different from what you proposed in that issue:
Within the concrete implementation the spread parameter is only available as a union type (this is not a problem because a) the implementation usually isn't even in TypeScript, and b) flow analysis is great and constantly improving). The important thing is exposing a well typed API for the consumer, which is very painful right now. |
You may also be interested in proposal #6229 |
Rest tuples are much more expressive now and handle this as well as we can. You'll need to use the |
@RyanCavanaugh can you provide an example of the classic "variable number of args followed by a callback"? EG: #1360 |
See examples from initial post #39094 and comment below Callback last is // Inferring parts of tuple types
declare function foo<T extends string[], U>(...args: [...T, () => void]): T;
foo(() => {}); // []
foo('hello', 'world', () => {}); // ["hello", "world"]
foo('hello', 42, () => {}); // Error, number not assignable to string |
Motivation:
Some JavaScript APIs take arguments of type Array, where the array is constructed, for example, by taking a number of items of one type followed by one or more arguments of another type, or perhaps the other way around. A real world example of this is the array-based dependency injection functionality in AngularJS, where you pass in an array consisting of a variable number of strings, followed by a function.
Motivating real world example:
Suggestion:
Enhancement to the Tuple type annotation capability to support variable numbers of type arguments to support the above scenario without a fixed set of overloads.
Example tuple typing:
Edited: moved the ... token to the left of the type reference to more closely match the variable function argument syntax.
Edited 12/3/2014: examples foo3, foo4, and foo5 would be possible, but not likely to be used and bad practice. Documented this and separated them out from the first two examples, which are the real motivating ones.
Additional note: syncing function arguments with the number of type arguments in a tuple type is beyond the scope of this suggestion.
The text was updated successfully, but these errors were encountered: