Add a generic "rest" type parameter, for use with Tuples. The goal of this feature is to provide better type support for certain operations that either cannot be reliably typed today, or could be more easily written.
A generic "rest" type parameter is written in the form ...T
. When generic types are inferred, this would be expanded into T0, T1, ... Tn
. This type parameter can only be used inside of a tuple type, using the form [...T]
, which would be expanded into [T0, T1, ... Tn]
.
Currently, if you wanted to write something like bind
with generics, you have two options:
// (1) non-generic
function bind(func: (...args: any[]) => any, thisArg?: any): (...args: any[]) => any;
// (2) generic
function bind<T>(func: () => T, thisArg?: any): () => T;
function bind<A, T>(func: (a: A) => T, thisArg?: any): (a: A) => T;
function bind<A, B, T>(func: (a: A, b: B) => T, thisArg?: any): (a: A, b: B) => T;
function bind<A, B, C, T>(func: (a: A, b: B, c: C) => T, thisArg?: any): (a: A, b: B, c: C) => T;
// ...
Option 1 provides little to no type safety. Option 2 provides better type safety, but must be constantly maintained as new uses are introduced that need more overloads to support more arguments.
With a generic "rest" type, you could instead write:
function bind<...T, U>(func: (...args: [...T]) => U, thisArg?: any): (...args: [...T]) => U;
function a(): boolean;
function b(x: number): boolean;
function c(x: number, y: string): boolean;
let A = bind(a, {}); // () => boolean;
let B = bind(b, {}); // (_0: number) => boolean;
let C = bind(c, {}); // (_0: number, _1: string) => boolean;
The Promise.all
helper suffers a similar issue, where you lose type safety:
class Promise<T> {
all<T>(promises: (T | IPromise<T>)[]): Promise<T[]>;
}
let a: Promise<number>;
let b: Promise<string>;
let c: Promise<MyClass>;
// The type argument for type parameter 'T' cannot be inferred from the usage...
Promise.all([a, b, c]).then(([a, b, c]) => {
});
With a generic "rest" parameter, the following would possible:
declare class Promise<T> {
public static all<...T>(promises: [...(T | IPromise<T>)]): Promise<[...T]>;
}
let a: Promise<number>;
let b: Promise<string>;
let c: Promise<MyClass>;
Promise.all([a, b, c]).then(([a, b, c]) => {
a; // number
b; // string
c; // MyClass
});