Tuples in rest parameters and spread expressions by ahejlsberg · Pull Request #24897 · microsoft/TypeScript (original) (raw)
This PR includes the following:
- Expansion of rest parameters with tuple types into discrete parameters.
- Expansion of spread expressions with tuple types into discrete arguments.
- Generic rest parameters and corresponding inference of tuple types.
- Optional elements in tuple types.
- Rest elements in tuple types.
With these features it becomes possible to strongly type a number of higher-order functions that transform functions and their parameter lists (such as JavaScript's bind
, call
, and apply
).
The PR implements the a number of the capabilities discussed in #5453, but with considerably less complexity.
Rest parameters with tuple types
When a rest parameter has a tuple type, the tuple type is expanded into a sequence of discrete parameters. For example the following two declarations are equivalent:
declare function foo(...args: [number, string, boolean]): void; declare function foo(args_0: number, args_1: string, args_2: boolean): void;
EDIT: With #26676 the type of rest parameter can be a union of tuple types. This effectively provides a form of overloading expressed in a single function signature.
Spread expressions with tuple types
When a function call includes a spread expression of a tuple type as the last argument, the spread expression corresponds to a sequence of discrete arguments of the tuple element types. Thus, the following calls are equivalent:
const args: [number, string, boolean] = [42, "hello", true]; foo(42, "hello", true); foo(args[0], args[1], args[2]); foo(...args);
Generic rest parameters
A rest parameter is permitted to have a generic type that is constrained to an array type, and type inference can infer tuple types for such generic rest parameters. This enables higher-order capturing and spreading of partial parameter lists:
declare function bind<T, U extends any[], V>(f: (x: T, ...args: U) => V, x: T): (...args: U) => V;
declare function f3(x: number, y: string, z: boolean): void;
const f2 = bind(f3, 42); // (y: string, z: boolean) => void const f1 = bind(f2, "hello"); // (z: boolean) => void const f0 = bind(f1, true); // () => void
f3(42, "hello", true); f2("hello", true); f1(true); f0();
In the declaration of f2
above, type inference infers types number
, [string, boolean]
and void
for T
, U
and V
respectively.
Note that when a tuple type is inferred from a sequence of parameters and later expanded into a parameter list, as is the case for U
, the original parameter names are used in the expansion (however, the names have no semantic meaning and are not otherwise observable).
Optional elements in tuple types
Tuple types now permit a ?
postfix on element types to indicate that the element is optional:
let t: [number, string?, boolean?]; t = [42, "hello", true]; t = [42, "hello"]; t = [42];
In --strictNullChecks
mode, a ?
modifier automatically includes undefined
in the element type, similar to optional parameters.
A tuple type permits an element to be omitted if it has a postfix ?
modifier on its type and all elements to the right of it also have ?
modifiers.
When tuple types are inferred for rest parameters, optional parameters in the source become optional tuple elements in the inferred type.
The length
property of a tuple type with optional elements is a union of numeric literal types representing the possible lengths. For example, the type of the length
property in the tuple type [number, string?, boolean?]
is 1 | 2 | 3
.
Rest elements in tuple types
EDIT: Updated to reflect change from string*
to ...string[]
syntax for rest elements.
The last element of a tuple type can be a rest element of the form ...X
, where X
is an array type. A rest element indicates that the tuple type is open-ended and may have zero or more additional elements of the array element type. For example, [number, ...string[]]
means tuples with a number
element followed by any number of string
elements.
function tuple<T extends any[]>(...args: T): T { return args; }
const numbers: number[] = getArrayOfNumbers(); const t1 = tuple("foo", 1, true); // [string, number, boolean] const t2 = tuple("bar", ...numbers); // [string, ...number[]]
The type of the length
property of a tuple type with a rest element is number
.
Strong typing of bind, call, and apply
With this PR it becomes possible to strongly type the bind
, call
, and apply
methods on function objects. This is however a breaking change for some existing code so we need to investigate the repercussions.