Skip to content

Commit

Permalink
Better typings for Array.concat(), etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
jablko committed Feb 2, 2020
1 parent 1450ac4 commit bdc9366
Show file tree
Hide file tree
Showing 40 changed files with 160 additions and 340 deletions.
1 change: 1 addition & 0 deletions src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,7 @@ namespace FourSlashInterface {
varEntry("JSON"),
interfaceEntry("ReadonlyArray"),
interfaceEntry("ConcatArray"),
typeEntry("Flatten"),
varEntry("Array"),
interfaceEntry("ArrayConstructor"),
interfaceEntry("TypedPropertyDescriptor"),
Expand Down
182 changes: 20 additions & 162 deletions src/lib/es2019.array.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,106 +10,23 @@ interface ReadonlyArray<T> {
* @param thisArg An object to which the this keyword can refer in the callback function. If
* thisArg is omitted, undefined is used as the this value.
*/
flatMap<U, This = undefined> (
callback: (this: This, value: T, index: number, array: T[]) => U | ReadonlyArray<U>,
thisArg?: This
): U[]


/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this:
ReadonlyArray<U[][][][]> |

ReadonlyArray<ReadonlyArray<U[][][]>> |
ReadonlyArray<ReadonlyArray<U[][]>[]> |
ReadonlyArray<ReadonlyArray<U[]>[][]> |
ReadonlyArray<ReadonlyArray<U>[][][]> |

ReadonlyArray<ReadonlyArray<ReadonlyArray<U[][]>>> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<U>[][]>> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>[][]> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<U>[]>[]> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<U[]>>[]> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<U[]>[]>> |

ReadonlyArray<ReadonlyArray<ReadonlyArray<ReadonlyArray<U[]>>>> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<ReadonlyArray<U>[]>>> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>[]>> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>>[]> |

ReadonlyArray<ReadonlyArray<ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>>>>,
depth: 4): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this:
ReadonlyArray<U[][][]> |

ReadonlyArray<ReadonlyArray<U>[][]> |
ReadonlyArray<ReadonlyArray<U[]>[]> |
ReadonlyArray<ReadonlyArray<U[][]>> |

ReadonlyArray<ReadonlyArray<ReadonlyArray<U[]>>> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<U>[]>> |
ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>[]> |

ReadonlyArray<ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>>>,
depth: 3): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this:
ReadonlyArray<U[][]> |

ReadonlyArray<ReadonlyArray<U[]>> |
ReadonlyArray<ReadonlyArray<U>[]> |

ReadonlyArray<ReadonlyArray<ReadonlyArray<U>>>,
depth: 2): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this:
ReadonlyArray<U[]> |
ReadonlyArray<ReadonlyArray<U>>,
depth?: 1
): U[];
flatMap<U> (
callbackfn: (value: T, index: number, array: readonly T[]) => U,
thisArg?: any
): Flatten<U>[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this:
ReadonlyArray<U>,
depth: 0
): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth. If no depth is provided, flat method defaults to the depth of 1.
*
* @param depth The maximum recursion depth
*/
flat<U>(depth?: number): any[];
flat(depth: 4): Flatten<Flatten<Flatten<Flatten<T>>>>[];
flat(depth: 3): Flatten<Flatten<Flatten<T>>>[];
flat(depth: 2): Flatten<Flatten<T>>[];
flat(depth?: 1): Flatten<T>[];
flat(depth: 0): T[];
flat(depth: number): Flatten<Flatten<Flatten<Flatten<T>>>> extends readonly any[] ? unknown[] : Flatten<Flatten<Flatten<Flatten<T>>>>[] | Flatten<Flatten<Flatten<T>>>[] | Flatten<Flatten<T>>[] | Flatten<T>[] | T[];
}

interface Array<T> {
Expand All @@ -124,80 +41,21 @@ interface Array<T> {
* @param thisArg An object to which the this keyword can refer in the callback function. If
* thisArg is omitted, undefined is used as the this value.
*/
flatMap<U, This = undefined> (
callback: (this: This, value: T, index: number, array: T[]) => U | ReadonlyArray<U>,
thisArg?: This
): U[]
flatMap<U> (
callbackfn: (value: T, index: number, array: T[]) => U,
thisArg?: any
): Flatten<U>[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[][][][][][][][], depth: 7): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[][][][][][][], depth: 6): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[][][][][][], depth: 5): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[][][][][], depth: 4): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[][][][], depth: 3): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[][][], depth: 2): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[][], depth?: 1): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth.
*
* @param depth The maximum recursion depth
*/
flat<U>(this: U[], depth: 0): U[];

/**
* Returns a new array with all sub-array elements concatenated into it recursively up to the
* specified depth. If no depth is provided, flat method defaults to the depth of 1.
*
* @param depth The maximum recursion depth
*/
flat<U>(depth?: number): any[];
flat(depth: 4): Flatten<Flatten<Flatten<Flatten<T>>>>[];
flat(depth: 3): Flatten<Flatten<Flatten<T>>>[];
flat(depth: 2): Flatten<Flatten<T>>[];
flat(depth?: 1): Flatten<T>[];
flat(depth: 0): T[];
flat(depth: number): Flatten<Flatten<Flatten<Flatten<T>>>> extends readonly any[] ? unknown[] : Flatten<Flatten<Flatten<Flatten<T>>>>[] | Flatten<Flatten<Flatten<T>>>[] | Flatten<Flatten<T>>[] | Flatten<T>[] | T[];
}
13 changes: 3 additions & 10 deletions src/lib/es5.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1081,11 +1081,7 @@ interface ReadonlyArray<T> {
* @param items Additional items to add to the end of array1.
*/
concat(...items: ConcatArray<T>[]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | ConcatArray<T>)[]): T[];
concat<U extends any[]>(...items: U): (T | Flatten<U[number]>)[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
Expand Down Expand Up @@ -1187,6 +1183,7 @@ interface ConcatArray<T> {
join(separator?: string): string;
slice(start?: number, end?: number): T[];
}
type Flatten<T> = T extends undefined ? T : T extends ConcatArray<infer U> ? U : T;

interface Array<T> {
/**
Expand Down Expand Up @@ -1215,11 +1212,7 @@ interface Array<T> {
* @param items Additional items to add to the end of array1.
*/
concat(...items: ConcatArray<T>[]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | ConcatArray<T>)[]): T[];
concat<U extends any[]>(...items: U): (T | Flatten<U[number]>)[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/arrayConcat2.types
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ var a: string[] = [];

a.concat("hello", 'world');
>a.concat("hello", 'world') : string[]
>a.concat : { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }
>a.concat : { (...items: ConcatArray<string>[]): string[]; <U extends any[]>(...items: U): (string | Flatten<U[number]>)[]; }
>a : string[]
>concat : { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }
>concat : { (...items: ConcatArray<string>[]): string[]; <U extends any[]>(...items: U): (string | Flatten<U[number]>)[]; }
>"hello" : "hello"
>'world' : "world"

a.concat('Hello');
>a.concat('Hello') : string[]
>a.concat : { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }
>a.concat : { (...items: ConcatArray<string>[]): string[]; <U extends any[]>(...items: U): (string | Flatten<U[number]>)[]; }
>a : string[]
>concat : { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }
>concat : { (...items: ConcatArray<string>[]): string[]; <U extends any[]>(...items: U): (string | Flatten<U[number]>)[]; }
>'Hello' : "Hello"

var b = new Array<string>();
Expand All @@ -25,8 +25,8 @@ var b = new Array<string>();

b.concat('hello');
>b.concat('hello') : string[]
>b.concat : { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }
>b.concat : { (...items: ConcatArray<string>[]): string[]; <U extends any[]>(...items: U): (string | Flatten<U[number]>)[]; }
>b : string[]
>concat : { (...items: ConcatArray<string>[]): string[]; (...items: (string | ConcatArray<string>)[]): string[]; }
>concat : { (...items: ConcatArray<string>[]): string[]; <U extends any[]>(...items: U): (string | Flatten<U[number]>)[]; }
>'hello' : "hello"

4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayConcat3.types
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ function doStuff<T extends object, T1 extends T>(a: Array<Fn<T>>, b: Array<Fn<T1

b.concat(a);
>b.concat(a) : Fn<T1>[]
>b.concat : { (...items: ConcatArray<Fn<T1>>[]): Fn<T1>[]; (...items: (Fn<T1> | ConcatArray<Fn<T1>>)[]): Fn<T1>[]; }
>b.concat : { (...items: ConcatArray<Fn<T1>>[]): Fn<T1>[]; <U extends any[]>(...items: U): (Fn<T1> | Flatten<U[number]>)[]; }
>b : Fn<T1>[]
>concat : { (...items: ConcatArray<Fn<T1>>[]): Fn<T1>[]; (...items: (Fn<T1> | ConcatArray<Fn<T1>>)[]): Fn<T1>[]; }
>concat : { (...items: ConcatArray<Fn<T1>>[]): Fn<T1>[]; <U extends any[]>(...items: U): (Fn<T1> | Flatten<U[number]>)[]; }
>a : Fn<T>[]
}

4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayConcatMap.types
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ var x = [].concat([{ a: 1 }], [{ a: 2 }])
>[].concat([{ a: 1 }], [{ a: 2 }]) .map(b => b.a) : any[]
>[].concat([{ a: 1 }], [{ a: 2 }]) .map : <U>(callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any) => U[]
>[].concat([{ a: 1 }], [{ a: 2 }]) : any[]
>[].concat : { (...items: ConcatArray<any>[]): any[]; (...items: any[]): any[]; }
>[].concat : { (...items: ConcatArray<any>[]): any[]; <U extends any[]>(...items: U): any[]; }
>[] : undefined[]
>concat : { (...items: ConcatArray<any>[]): any[]; (...items: any[]): any[]; }
>concat : { (...items: ConcatArray<any>[]): any[]; <U extends any[]>(...items: U): any[]; }
>[{ a: 1 }] : { a: number; }[]
>{ a: 1 } : { a: number; }
>a : number
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/arrayFlatMap.types
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ const readonlyArray: ReadonlyArray<number> = [];

array.flatMap((): ReadonlyArray<number> => []); // ok
>array.flatMap((): ReadonlyArray<number> => []) : number[]
>array.flatMap : <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
>array.flatMap : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => Flatten<U>[]
>array : number[]
>flatMap : <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
>flatMap : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => Flatten<U>[]
>(): ReadonlyArray<number> => [] : () => readonly number[]
>[] : undefined[]

readonlyArray.flatMap((): ReadonlyArray<number> => []); // ok
>readonlyArray.flatMap((): ReadonlyArray<number> => []) : number[]
>readonlyArray.flatMap : <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
>readonlyArray.flatMap : <U>(callbackfn: (value: number, index: number, array: readonly number[]) => U, thisArg?: any) => Flatten<U>[]
>readonlyArray : readonly number[]
>flatMap : <U, This = undefined>(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This) => U[]
>flatMap : <U>(callbackfn: (value: number, index: number, array: readonly number[]) => U, thisArg?: any) => Flatten<U>[]
>(): ReadonlyArray<number> => [] : () => readonly number[]
>[] : undefined[]

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'readonly B[]'.
Property 'b' is missing in type 'A' but required in type 'B'.
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C<A>' is not assignable to type 'readonly B[]'.
The types returned by 'concat(...)' are incompatible between these types.
The types returned by 'slice(...)' are incompatible between these types.
Type 'A[]' is not assignable to type 'B[]'.
Type 'A' is not assignable to type 'B'.

Expand Down Expand Up @@ -31,7 +31,7 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
rrb = cra; // error: 'A' is not assignable to 'B'
~~~
!!! error TS2322: Type 'C<A>' is not assignable to type 'readonly B[]'.
!!! error TS2322: The types returned by 'concat(...)' are incompatible between these types.
!!! error TS2322: The types returned by 'slice(...)' are incompatible between these types.
!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'.
!!! error TS2322: Type 'A' is not assignable to type 'B'.

8 changes: 4 additions & 4 deletions tests/baselines/reference/concatError.types
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ fa = fa.concat([0]);
>fa = fa.concat([0]) : number[]
>fa : number[]
>fa.concat([0]) : number[]
>fa.concat : { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }
>fa.concat : { (...items: ConcatArray<number>[]): number[]; <U extends any[]>(...items: U): (number | Flatten<U[number]>)[]; }
>fa : number[]
>concat : { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }
>concat : { (...items: ConcatArray<number>[]): number[]; <U extends any[]>(...items: U): (number | Flatten<U[number]>)[]; }
>[0] : number[]
>0 : 0

fa = fa.concat(0);
>fa = fa.concat(0) : number[]
>fa : number[]
>fa.concat(0) : number[]
>fa.concat : { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }
>fa.concat : { (...items: ConcatArray<number>[]): number[]; <U extends any[]>(...items: U): (number | Flatten<U[number]>)[]; }
>fa : number[]
>concat : { (...items: ConcatArray<number>[]): number[]; (...items: (number | ConcatArray<number>)[]): number[]; }
>concat : { (...items: ConcatArray<number>[]): number[]; <U extends any[]>(...items: U): (number | Flatten<U[number]>)[]; }
>0 : 0


Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/concatTuples.types
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ ijs = ijs.concat([[3, 4], [5, 6]]);
>ijs = ijs.concat([[3, 4], [5, 6]]) : [number, number][]
>ijs : [number, number][]
>ijs.concat([[3, 4], [5, 6]]) : [number, number][]
>ijs.concat : { (...items: ConcatArray<[number, number]>[]): [number, number][]; (...items: ([number, number] | ConcatArray<[number, number]>)[]): [number, number][]; }
>ijs.concat : { (...items: ConcatArray<[number, number]>[]): [number, number][]; <U extends any[]>(...items: U): ([number, number] | Flatten<U[number]>)[]; }
>ijs : [number, number][]
>concat : { (...items: ConcatArray<[number, number]>[]): [number, number][]; (...items: ([number, number] | ConcatArray<[number, number]>)[]): [number, number][]; }
>concat : { (...items: ConcatArray<[number, number]>[]): [number, number][]; <U extends any[]>(...items: U): ([number, number] | Flatten<U[number]>)[]; }
>[[3, 4], [5, 6]] : [number, number][]
>[3, 4] : [number, number]
>3 : 3
Expand Down
Loading

0 comments on commit bdc9366

Please sign in to comment.