Skip to content

Feature Request: Tuples with variable type arguments #1336

Closed
@JeroMiya

Description

@JeroMiya

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:

angular.module('app').controller(['$scope', function($scope: ng.IScope) { /*etc...*/ }]);

// how we might type this in 1.3:
interface IModule {
  controller(injectable: () => any);
  controller(injectable: [string, () => any]);
  controller(injectable: [string, string, () => any]);
  controller(injectable: [string, string, string, () => any]);
  controller(injectable: [string, string, string, string, () => any]);
  // and so on, until we have 'enough' overloads to cover common usage
}

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:

// same as controller function in the 1.3 example above
controller(injectable: () => any): void;
controller(injectable: [...string, () => any]): void {
  // type of expression injectable[n] is string | () => any
  // MAYBE special case for known indices: injectable[0] is type string and/or injectable[injection.length - 1] is () => any
}

// generalized examples (I don't have real-world examples of these, but might as well be comprehensive)
function foo1(variableLast: [() => any, ...string]) {
  // type of expression variableLast[0] is () => any
  // type of expression variableLast[n] is () => any | string
}
function foo2(variableFirst: [...string, () => any]) {}

// these forms might be possible, but doubtful there are real-world examples
// of these in JS apis, and would be bad practice in general. I would be OK if these
// forms were not allowed:
function foo3(variableMiddle: [string, ...number, () => any]) {
  // type of expression variableMiddle[0] is string
  // type of expression variable[n] is string | number | () => any
}
function foo4(twoVariables: [...string, ...number]) {}
function foo5(fixedInMiddleOfVariables: [...string, number, ...string]) {}

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.SuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions