Skip to content

Commit 537999c

Browse files
committed
Added overload to ReadonlyArray.prototype.every to make it a type guard if the callback is one.
Also made the type of thisArg generic. Fixes microsoft#14963
1 parent eb80799 commit 537999c

10 files changed

+176
-36
lines changed

src/lib/es5.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,8 @@ interface ReadonlyArray<T> {
10141014
* @param callbackfn A function that accepts up to three arguments. The every method calls the callbackfn function for each element in array1 until the callbackfn returns false, or until the end of the array.
10151015
* @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.
10161016
*/
1017-
every(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => boolean, thisArg?: any): boolean;
1017+
every<Z, U extends T>(callbackfn: (this: Z, value: T, index: number, array: ReadonlyArray<T>) => value is U, thisArg?: Z): this is ReadonlyArray<U>;
1018+
every<Z>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => boolean, thisArg?: Z): boolean;
10181019
/**
10191020
* Determines whether the specified callback function returns true for any element of an array.
10201021
* @param callbackfn A function that accepts up to three arguments. The some method calls the callbackfn function for each element in array1 until the callbackfn returns true, or until the end of the array.

tests/baselines/reference/2dArrays.symbols

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ class Board {
2525
>allShipsSunk : Symbol(Board.allShipsSunk, Decl(2dArrays.ts, 9, 18))
2626

2727
return this.ships.every(function (val) { return val.isSunk; });
28-
>this.ships.every : Symbol(Array.every, Decl(lib.d.ts, --, --))
28+
>this.ships.every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
2929
>this.ships : Symbol(Board.ships, Decl(2dArrays.ts, 7, 13))
3030
>this : Symbol(Board, Decl(2dArrays.ts, 5, 1))
3131
>ships : Symbol(Board.ships, Decl(2dArrays.ts, 7, 13))
32-
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
32+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
3333
>val : Symbol(val, Decl(2dArrays.ts, 12, 42))
3434
>val.isSunk : Symbol(Ship.isSunk, Decl(2dArrays.ts, 3, 12))
3535
>val : Symbol(val, Decl(2dArrays.ts, 12, 42))

tests/baselines/reference/2dArrays.types

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ class Board {
2626

2727
return this.ships.every(function (val) { return val.isSunk; });
2828
>this.ships.every(function (val) { return val.isSunk; }) : boolean
29-
>this.ships.every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
29+
>this.ships.every : { <Z, U extends Ship>(callbackfn: (this: Z, value: Ship, index: number, array: Ship[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: Z): boolean; }
3030
>this.ships : Ship[]
3131
>this : this
3232
>ships : Ship[]
33-
>every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
34-
>function (val) { return val.isSunk; } : (val: Ship) => boolean
33+
>every : { <Z, U extends Ship>(callbackfn: (this: Z, value: Ship, index: number, array: Ship[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: Z): boolean; }
34+
>function (val) { return val.isSunk; } : (this: {}, val: Ship) => boolean
3535
>val : Ship
3636
>val.isSunk : boolean
3737
>val : Ship
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
tests/cases/compiler/arrayEvery.ts(11,15): error TS2339: Property 'indexOf' does not exist on type 'number'.
2+
tests/cases/compiler/arrayEvery.ts(14,15): error TS2339: Property 'indexOf' does not exist on type 'number'.
3+
tests/cases/compiler/arrayEvery.ts(32,40): error TS2339: Property 'indexOf' does not exist on type 'number'.
4+
tests/cases/compiler/arrayEvery.ts(35,40): error TS2339: Property 'indexOf' does not exist on type 'number'.
5+
6+
7+
==== tests/cases/compiler/arrayEvery.ts (4 errors) ====
8+
declare const baseArray: Array<number | string>;
9+
declare const baseReadonlyArray: ReadonlyArray<number | string>;
10+
11+
baseArray.every(function (x) {
12+
return this.indexOf("f") > 0;
13+
}, "foo"); // first overload
14+
baseReadonlyArray.every(function (x) {
15+
return this.indexOf("f") > 0;
16+
}, "foo"); // first overload
17+
baseArray.every(function (x): x is number {
18+
return this.indexOf("f") > 0;
19+
~~~~~~~
20+
!!! error TS2339: Property 'indexOf' does not exist on type 'number'.
21+
}, 1); // error: thisArg type doesn't match
22+
baseReadonlyArray.every(function (x) {
23+
return this.indexOf("f") > 0;
24+
~~~~~~~
25+
!!! error TS2339: Property 'indexOf' does not exist on type 'number'.
26+
}, 1); // error: thisArg type doesn't match
27+
28+
if (baseArray.every((x): x is number => "number" === typeof x)) {
29+
const divArray = baseArray; // should be number[]
30+
}
31+
32+
if (baseReadonlyArray.every((x): x is number => "number" === typeof x)) {
33+
const divReadonlyArray = baseReadonlyArray; // should be ReadonlyArray<number>
34+
}
35+
36+
baseArray.every(function (x): x is number {
37+
return "number" === typeof x && this.indexOf("f") > 0;
38+
}, "foo"); // second overload using thisArg
39+
baseReadonlyArray.every(function (x): x is number {
40+
return "number" === typeof x && this.indexOf("f") > 0;
41+
}, "foo");
42+
baseArray.every(function (x): x is number {
43+
return "number" === typeof x && this.indexOf("f") > 0;
44+
~~~~~~~
45+
!!! error TS2339: Property 'indexOf' does not exist on type 'number'.
46+
}, 1); // error: thisArg type doesn't match
47+
baseReadonlyArray.every(function (x): x is number {
48+
return "number" === typeof x && this.indexOf("f") > 0;
49+
~~~~~~~
50+
!!! error TS2339: Property 'indexOf' does not exist on type 'number'.
51+
}, 1); // error: thisArg type doesn't match
+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//// [arrayEvery.ts]
2+
declare const baseArray: Array<number | string>;
3+
declare const baseReadonlyArray: ReadonlyArray<number | string>;
4+
5+
baseArray.every(function (x) {
6+
return this.indexOf("f") > 0;
7+
}, "foo"); // first overload
8+
baseReadonlyArray.every(function (x) {
9+
return this.indexOf("f") > 0;
10+
}, "foo"); // first overload
11+
baseArray.every(function (x): x is number {
12+
return this.indexOf("f") > 0;
13+
}, 1); // error: thisArg type doesn't match
14+
baseReadonlyArray.every(function (x) {
15+
return this.indexOf("f") > 0;
16+
}, 1); // error: thisArg type doesn't match
17+
18+
if (baseArray.every((x): x is number => "number" === typeof x)) {
19+
const divArray = baseArray; // should be number[]
20+
}
21+
22+
if (baseReadonlyArray.every((x): x is number => "number" === typeof x)) {
23+
const divReadonlyArray = baseReadonlyArray; // should be ReadonlyArray<number>
24+
}
25+
26+
baseArray.every(function (x): x is number {
27+
return "number" === typeof x && this.indexOf("f") > 0;
28+
}, "foo"); // second overload using thisArg
29+
baseReadonlyArray.every(function (x): x is number {
30+
return "number" === typeof x && this.indexOf("f") > 0;
31+
}, "foo");
32+
baseArray.every(function (x): x is number {
33+
return "number" === typeof x && this.indexOf("f") > 0;
34+
}, 1); // error: thisArg type doesn't match
35+
baseReadonlyArray.every(function (x): x is number {
36+
return "number" === typeof x && this.indexOf("f") > 0;
37+
}, 1); // error: thisArg type doesn't match
38+
39+
//// [arrayEvery.js]
40+
baseArray.every(function (x) {
41+
return this.indexOf("f") > 0;
42+
}, "foo"); // first overload
43+
baseReadonlyArray.every(function (x) {
44+
return this.indexOf("f") > 0;
45+
}, "foo"); // first overload
46+
baseArray.every(function (x) {
47+
return this.indexOf("f") > 0;
48+
}, 1); // error: thisArg type doesn't match
49+
baseReadonlyArray.every(function (x) {
50+
return this.indexOf("f") > 0;
51+
}, 1); // error: thisArg type doesn't match
52+
if (baseArray.every(function (x) { return "number" === typeof x; })) {
53+
var divArray = baseArray; // should be number[]
54+
}
55+
if (baseReadonlyArray.every(function (x) { return "number" === typeof x; })) {
56+
var divReadonlyArray = baseReadonlyArray; // should be ReadonlyArray<number>
57+
}
58+
baseArray.every(function (x) {
59+
return "number" === typeof x && this.indexOf("f") > 0;
60+
}, "foo"); // second overload using thisArg
61+
baseReadonlyArray.every(function (x) {
62+
return "number" === typeof x && this.indexOf("f") > 0;
63+
}, "foo");
64+
baseArray.every(function (x) {
65+
return "number" === typeof x && this.indexOf("f") > 0;
66+
}, 1); // error: thisArg type doesn't match
67+
baseReadonlyArray.every(function (x) {
68+
return "number" === typeof x && this.indexOf("f") > 0;
69+
}, 1); // error: thisArg type doesn't match

tests/baselines/reference/contextuallyTypedIife.symbols

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,17 @@
4747
// rest parameters
4848
((...numbers) => numbers.every(n => n > 0))(5,6,7);
4949
>numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 17, 2))
50-
>numbers.every : Symbol(Array.every, Decl(lib.d.ts, --, --))
50+
>numbers.every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
5151
>numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 17, 2))
52-
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
52+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
5353
>n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 31))
5454
>n : Symbol(n, Decl(contextuallyTypedIife.ts, 17, 31))
5555

5656
((...mixed) => mixed.every(n => !!n))(5,'oops','oh no');
5757
>mixed : Symbol(mixed, Decl(contextuallyTypedIife.ts, 18, 2))
58-
>mixed.every : Symbol(Array.every, Decl(lib.d.ts, --, --))
58+
>mixed.every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
5959
>mixed : Symbol(mixed, Decl(contextuallyTypedIife.ts, 18, 2))
60-
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
60+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
6161
>n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 27))
6262
>n : Symbol(n, Decl(contextuallyTypedIife.ts, 18, 27))
6363

tests/baselines/reference/contextuallyTypedIife.types

+6-6
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@
105105
>(...numbers) => numbers.every(n => n > 0) : (...numbers: number[]) => boolean
106106
>numbers : number[]
107107
>numbers.every(n => n > 0) : boolean
108-
>numbers.every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean
108+
>numbers.every : { <Z, U extends number>(callbackfn: (this: Z, value: number, index: number, array: number[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: Z): boolean; }
109109
>numbers : number[]
110-
>every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean
111-
>n => n > 0 : (n: number) => boolean
110+
>every : { <Z, U extends number>(callbackfn: (this: Z, value: number, index: number, array: number[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: Z): boolean; }
111+
>n => n > 0 : (this: {}, n: number) => boolean
112112
>n : number
113113
>n > 0 : boolean
114114
>n : number
@@ -123,10 +123,10 @@
123123
>(...mixed) => mixed.every(n => !!n) : (...mixed: (string | number)[]) => boolean
124124
>mixed : (string | number)[]
125125
>mixed.every(n => !!n) : boolean
126-
>mixed.every : (callbackfn: (value: string | number, index: number, array: (string | number)[]) => boolean, thisArg?: any) => boolean
126+
>mixed.every : { <Z, U extends string | number>(callbackfn: (this: Z, value: string | number, index: number, array: (string | number)[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => boolean, thisArg?: Z): boolean; }
127127
>mixed : (string | number)[]
128-
>every : (callbackfn: (value: string | number, index: number, array: (string | number)[]) => boolean, thisArg?: any) => boolean
129-
>n => !!n : (n: string | number) => boolean
128+
>every : { <Z, U extends string | number>(callbackfn: (this: Z, value: string | number, index: number, array: (string | number)[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => boolean, thisArg?: Z): boolean; }
129+
>n => !!n : (this: {}, n: string | number) => boolean
130130
>n : string | number
131131
>!!n : boolean
132132
>!n : boolean

tests/baselines/reference/targetTypeArgs.symbols

+8-8
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,29 @@ foo(function(x) { x });
2222
>v : Symbol(v, Decl(targetTypeArgs.ts, 6, 21))
2323

2424
["hello"].every(function(v,i,a) {return true;});
25-
>["hello"].every : Symbol(Array.every, Decl(lib.d.ts, --, --))
26-
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
25+
>["hello"].every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
26+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
2727
>v : Symbol(v, Decl(targetTypeArgs.ts, 7, 25))
2828
>i : Symbol(i, Decl(targetTypeArgs.ts, 7, 27))
2929
>a : Symbol(a, Decl(targetTypeArgs.ts, 7, 29))
3030

3131
[1].every(function(v,i,a) {return true;});
32-
>[1].every : Symbol(Array.every, Decl(lib.d.ts, --, --))
33-
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
32+
>[1].every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
33+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
3434
>v : Symbol(v, Decl(targetTypeArgs.ts, 8, 19))
3535
>i : Symbol(i, Decl(targetTypeArgs.ts, 8, 21))
3636
>a : Symbol(a, Decl(targetTypeArgs.ts, 8, 23))
3737

3838
[1].every(function(v,i,a) {return true;});
39-
>[1].every : Symbol(Array.every, Decl(lib.d.ts, --, --))
40-
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
39+
>[1].every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
40+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
4141
>v : Symbol(v, Decl(targetTypeArgs.ts, 9, 19))
4242
>i : Symbol(i, Decl(targetTypeArgs.ts, 9, 21))
4343
>a : Symbol(a, Decl(targetTypeArgs.ts, 9, 23))
4444

4545
["s"].every(function(v,i,a) {return true;});
46-
>["s"].every : Symbol(Array.every, Decl(lib.d.ts, --, --))
47-
>every : Symbol(Array.every, Decl(lib.d.ts, --, --))
46+
>["s"].every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
47+
>every : Symbol(Array.every, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
4848
>v : Symbol(v, Decl(targetTypeArgs.ts, 10, 21))
4949
>i : Symbol(i, Decl(targetTypeArgs.ts, 10, 23))
5050
>a : Symbol(a, Decl(targetTypeArgs.ts, 10, 25))

tests/baselines/reference/targetTypeArgs.types

+12-12
Original file line numberDiff line numberDiff line change
@@ -31,47 +31,47 @@ foo(function(x) { x });
3131

3232
["hello"].every(function(v,i,a) {return true;});
3333
>["hello"].every(function(v,i,a) {return true;}) : boolean
34-
>["hello"].every : (callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: any) => boolean
34+
>["hello"].every : { <Z, U extends string>(callbackfn: (this: Z, value: string, index: number, array: string[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: Z): boolean; }
3535
>["hello"] : string[]
3636
>"hello" : "hello"
37-
>every : (callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: any) => boolean
38-
>function(v,i,a) {return true;} : (v: string, i: number, a: string[]) => true
37+
>every : { <Z, U extends string>(callbackfn: (this: Z, value: string, index: number, array: string[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: Z): boolean; }
38+
>function(v,i,a) {return true;} : (this: {}, v: string, i: number, a: string[]) => true
3939
>v : string
4040
>i : number
4141
>a : string[]
4242
>true : true
4343

4444
[1].every(function(v,i,a) {return true;});
4545
>[1].every(function(v,i,a) {return true;}) : boolean
46-
>[1].every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean
46+
>[1].every : { <Z, U extends number>(callbackfn: (this: Z, value: number, index: number, array: number[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: Z): boolean; }
4747
>[1] : number[]
4848
>1 : 1
49-
>every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean
50-
>function(v,i,a) {return true;} : (v: number, i: number, a: number[]) => true
49+
>every : { <Z, U extends number>(callbackfn: (this: Z, value: number, index: number, array: number[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: Z): boolean; }
50+
>function(v,i,a) {return true;} : (this: {}, v: number, i: number, a: number[]) => true
5151
>v : number
5252
>i : number
5353
>a : number[]
5454
>true : true
5555

5656
[1].every(function(v,i,a) {return true;});
5757
>[1].every(function(v,i,a) {return true;}) : boolean
58-
>[1].every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean
58+
>[1].every : { <Z, U extends number>(callbackfn: (this: Z, value: number, index: number, array: number[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: Z): boolean; }
5959
>[1] : number[]
6060
>1 : 1
61-
>every : (callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: any) => boolean
62-
>function(v,i,a) {return true;} : (v: number, i: number, a: number[]) => true
61+
>every : { <Z, U extends number>(callbackfn: (this: Z, value: number, index: number, array: number[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: number, index: number, array: number[]) => boolean, thisArg?: Z): boolean; }
62+
>function(v,i,a) {return true;} : (this: {}, v: number, i: number, a: number[]) => true
6363
>v : number
6464
>i : number
6565
>a : number[]
6666
>true : true
6767

6868
["s"].every(function(v,i,a) {return true;});
6969
>["s"].every(function(v,i,a) {return true;}) : boolean
70-
>["s"].every : (callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: any) => boolean
70+
>["s"].every : { <Z, U extends string>(callbackfn: (this: Z, value: string, index: number, array: string[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: Z): boolean; }
7171
>["s"] : string[]
7272
>"s" : "s"
73-
>every : (callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: any) => boolean
74-
>function(v,i,a) {return true;} : (v: string, i: number, a: string[]) => true
73+
>every : { <Z, U extends string>(callbackfn: (this: Z, value: string, index: number, array: string[]) => value is U, thisArg?: Z): this is U[]; <Z>(callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: Z): boolean; }
74+
>function(v,i,a) {return true;} : (this: {}, v: string, i: number, a: string[]) => true
7575
>v : string
7676
>i : number
7777
>a : string[]

tests/cases/compiler/arrayEvery.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
declare const baseReadonlyArray: ReadonlyArray<number | string>;
2+
3+
baseReadonlyArray.every(function (x) {
4+
return this.indexOf("f") > 0;
5+
}, "foo"); // first overload
6+
baseReadonlyArray.every(function (x) {
7+
return this.indexOf("f") > 0;
8+
}, 1); // error: thisArg type doesn't match
9+
10+
if (baseReadonlyArray.every((x): x is number => "number" === typeof x)) {
11+
const divReadonlyArray = baseReadonlyArray; // should be ReadonlyArray<number>
12+
}
13+
14+
baseReadonlyArray.every(function (x): x is number {
15+
return "number" === typeof x && this.indexOf("f") > 0;
16+
}, "foo");
17+
baseReadonlyArray.every(function (x): x is number {
18+
return "number" === typeof x && this.indexOf("f") > 0;
19+
}, 1); // error: thisArg type doesn't match

0 commit comments

Comments
 (0)