Skip to content

Commit c84758f

Browse files
committed
Better typings for Array.map()
1 parent 7c14aff commit c84758f

22 files changed

+336
-130
lines changed

src/lib/es5.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,7 @@ interface ReadonlyArray<T> {
11381138
* @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
11391139
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
11401140
*/
1141-
map<U>(callbackfn: (value: T, index: number, array: readonly T[]) => U, thisArg?: any): U[];
1141+
map<U>(callbackfn: (value: T, index: number, array: readonly T[]) => U, thisArg?: any): { -readonly [P in keyof this]: U } extends U[] ? { -readonly [P in keyof this]: U } : U[];
11421142
/**
11431143
* Returns the elements of an array that meet the condition specified in a callback function.
11441144
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.
@@ -1308,7 +1308,7 @@ interface Array<T> {
13081308
* @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
13091309
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
13101310
*/
1311-
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
1311+
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): { -readonly [P in keyof this]: U } extends U[] ? { -readonly [P in keyof this]: U } : U[];
13121312
/**
13131313
* Returns the elements of an array that meet the condition specified in a callback function.
13141314
* @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array.

tests/baselines/reference/arityAndOrderCompatibility01.errors.txt

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(15,12): error TS2493: Tuple type '[string, number]' of length '2' has no element at index '2'.
2+
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(16,5): error TS2461: Type 'StrNum' is not an array type.
23
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(17,5): error TS2461: Type '{ 0: string; 1: number; length: 2; }' is not an array type.
34
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(18,5): error TS2741: Property '2' is missing in type '[string, number]' but required in type '[number, number, number]'.
45
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(19,5): error TS2741: Property '2' is missing in type 'StrNum' but required in type '[number, number, number]'.
@@ -28,7 +29,7 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(31,5): error
2829
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(32,5): error TS2322: Type '{ 0: string; 1: number; length: 2; }' is not assignable to type '[number, string]'.
2930

3031

31-
==== tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts (17 errors) ====
32+
==== tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts (18 errors) ====
3233
interface StrNum extends Array<string|number> {
3334
0: string;
3435
1: number;
@@ -47,6 +48,8 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(32,5): error
4748
~
4849
!!! error TS2493: Tuple type '[string, number]' of length '2' has no element at index '2'.
4950
var [d, e, f] = y;
51+
~~~~~~~~~
52+
!!! error TS2461: Type 'StrNum' is not an array type.
5053
var [g, h, i] = z;
5154
~~~~~~~~~
5255
!!! error TS2461: Type '{ 0: string; 1: number; length: 2; }' is not an array type.

tests/baselines/reference/arityAndOrderCompatibility01.types

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ var [a, b, c] = x;
3636
>x : [string, number]
3737

3838
var [d, e, f] = y;
39-
>d : string
40-
>e : number
41-
>f : string | number
39+
>d : any
40+
>e : any
41+
>f : any
4242
>y : StrNum
4343

4444
var [g, h, i] = z;

tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
11
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'readonly B[]'.
22
Property 'b' is missing in type 'A' but required in type 'B'.
3+
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(15,1): error TS2322: Type 'C<A>' is not assignable to type 'readonly A[]'.
4+
The types returned by 'map(...)' are incompatible between these types.
5+
Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]' is not assignable to type 'U[]'.
6+
Type 'U[] | { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
7+
Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
8+
Types of property 'length' are incompatible.
9+
Type 'U' is not assignable to type 'number'.
10+
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(16,1): error TS2322: Type 'C<B>' is not assignable to type 'readonly A[]'.
11+
The types returned by 'map(...)' are incompatible between these types.
12+
Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]' is not assignable to type 'U[]'.
13+
Type 'U[] | { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
14+
Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
15+
Types of property 'length' are incompatible.
16+
Type 'U' is not assignable to type 'number'.
17+
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(17,1): error TS2322: Type 'C<B>' is not assignable to type 'readonly B[]'.
18+
The types returned by 'map(...)' are incompatible between these types.
19+
Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]' is not assignable to type 'U[]'.
320
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C<A>' is not assignable to type 'readonly B[]'.
421
The types returned by 'concat(...)' are incompatible between these types.
522
Type 'A[]' is not assignable to type 'B[]'.
623
Type 'A' is not assignable to type 'B'.
724

825

9-
==== tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts (2 errors) ====
26+
==== tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts (5 errors) ====
1027
class A { a }
1128
class B extends A { b }
1229
class C<T> extends Array<T> { c }
@@ -26,8 +43,28 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
2643
!!! related TS2728 tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts:2:21: 'b' is declared here.
2744

2845
rra = cra;
46+
~~~
47+
!!! error TS2322: Type 'C<A>' is not assignable to type 'readonly A[]'.
48+
!!! error TS2322: The types returned by 'map(...)' are incompatible between these types.
49+
!!! error TS2322: Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]' is not assignable to type 'U[]'.
50+
!!! error TS2322: Type 'U[] | { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
51+
!!! error TS2322: Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
52+
!!! error TS2322: Types of property 'length' are incompatible.
53+
!!! error TS2322: Type 'U' is not assignable to type 'number'.
2954
rra = crb; // OK, C<B> is assignable to ReadonlyArray<A>
55+
~~~
56+
!!! error TS2322: Type 'C<B>' is not assignable to type 'readonly A[]'.
57+
!!! error TS2322: The types returned by 'map(...)' are incompatible between these types.
58+
!!! error TS2322: Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]' is not assignable to type 'U[]'.
59+
!!! error TS2322: Type 'U[] | { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
60+
!!! error TS2322: Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; }' is not assignable to type 'U[]'.
61+
!!! error TS2322: Types of property 'length' are incompatible.
62+
!!! error TS2322: Type 'U' is not assignable to type 'number'.
3063
rrb = crb;
64+
~~~
65+
!!! error TS2322: Type 'C<B>' is not assignable to type 'readonly B[]'.
66+
!!! error TS2322: The types returned by 'map(...)' are incompatible between these types.
67+
!!! error TS2322: Type '{ [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; c: U; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]' is not assignable to type 'U[]'.
3168
rrb = cra; // error: 'A' is not assignable to 'B'
3269
~~~
3370
!!! error TS2322: Type 'C<A>' is not assignable to type 'readonly B[]'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
tests/cases/compiler/bestChoiceType.ts(3,23): error TS2349: This expression is not callable.
2+
Each member of the union type '(<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])' has signatures, but none of those signatures are compatible with each other.
3+
tests/cases/compiler/bestChoiceType.ts(10,15): error TS2349: This expression is not callable.
4+
Each member of the union type '(<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])' has signatures, but none of those signatures are compatible with each other.
5+
tests/cases/compiler/bestChoiceType.ts(16,15): error TS2349: This expression is not callable.
6+
Each member of the union type '(<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])' has signatures, but none of those signatures are compatible with each other.
7+
8+
9+
==== tests/cases/compiler/bestChoiceType.ts (3 errors) ====
10+
// Repro from #10041
11+
12+
(''.match(/ /) || []).map(s => s.toLowerCase());
13+
~~~
14+
!!! error TS2349: This expression is not callable.
15+
!!! error TS2349: Each member of the union type '(<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])' has signatures, but none of those signatures are compatible with each other.
16+
17+
// Similar cases
18+
19+
function f1() {
20+
let x = ''.match(/ /);
21+
let y = x || [];
22+
let z = y.map(s => s.toLowerCase());
23+
~~~
24+
!!! error TS2349: This expression is not callable.
25+
!!! error TS2349: Each member of the union type '(<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])' has signatures, but none of those signatures are compatible with each other.
26+
}
27+
28+
function f2() {
29+
let x = ''.match(/ /);
30+
let y = x ? x : [];
31+
let z = y.map(s => s.toLowerCase());
32+
~~~
33+
!!! error TS2349: This expression is not callable.
34+
!!! error TS2349: Each member of the union type '(<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } extends U[] ? { [x: number]: U; index?: U | undefined; input?: U | undefined; length: U; toString: U; toLocaleString: U; pop: U; push: U; concat: U; join: U; reverse: U; shift: U; slice: U; sort: U; splice: U; unshift: U; indexOf: U; lastIndexOf: U; every: U; some: U; forEach: U; map: U; filter: U; reduce: U; reduceRight: U; } : U[]) | (<U>(callbackfn: (value: never, index: number, array: never[]) => U, thisArg?: any) => U[])' has signatures, but none of those signatures are compatible with each other.
35+
}
36+

0 commit comments

Comments
 (0)