Skip to content

Commit 0bc29ac

Browse files
dmitri-gbsandersn
andauthored
Add a type-guard overload of Array.every (#38200)
Co-authored-by: Nathan Shively-Sanders <[email protected]>
1 parent 4105d32 commit 0bc29ac

29 files changed

+166
-71
lines changed

src/lib/es5.d.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,15 @@ interface ReadonlyArray<T> {
11091109
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array.
11101110
*/
11111111
lastIndexOf(searchElement: T, fromIndex?: number): number;
1112+
/**
1113+
* Determines whether all the members of an array satisfy the specified test.
1114+
* @param callbackfn A function that accepts up to three arguments. The every method calls
1115+
* the callbackfn function for each element in the array until the callbackfn returns a value
1116+
* which is coercible to the Boolean value false, or until the end of the array.
1117+
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
1118+
* If thisArg is omitted, undefined is used as the this value.
1119+
*/
1120+
every<S extends T>(callbackfn: (value: T, index: number, array: readonly T[]) => value is S, thisArg?: any): this is readonly S[];
11121121
/**
11131122
* Determines whether all the members of an array satisfy the specified test.
11141123
* @param callbackfn A function that accepts up to three arguments. The every method calls
@@ -1279,6 +1288,15 @@ interface Array<T> {
12791288
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array.
12801289
*/
12811290
lastIndexOf(searchElement: T, fromIndex?: number): number;
1291+
/**
1292+
* Determines whether all the members of an array satisfy the specified test.
1293+
* @param callbackfn A function that accepts up to three arguments. The every method calls
1294+
* the callbackfn function for each element in the array until the callbackfn returns a value
1295+
* which is coercible to the Boolean value false, or until the end of the array.
1296+
* @param thisArg An object to which the this keyword can refer in the callbackfn function.
1297+
* If thisArg is omitted, undefined is used as the this value.
1298+
*/
1299+
every<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): this is S[];
12821300
/**
12831301
* Determines whether all the members of an array satisfy the specified test.
12841302
* @param callbackfn A function that accepts up to three arguments. The every method calls

tests/baselines/reference/2dArrays.symbols

Lines changed: 2 additions & 2 deletions
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.es5.d.ts, --, --))
28+
>this.ships.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.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.es5.d.ts, --, --))
32+
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ class Board {
2424

2525
return this.ships.every(function (val) { return val.isSunk; });
2626
>this.ships.every(function (val) { return val.isSunk; }) : boolean
27-
>this.ships.every : (callbackfn: (value: Ship, index: number, array: Ship[]) => unknown, thisArg?: any) => boolean
27+
>this.ships.every : { <S extends Ship>(callbackfn: (value: Ship, index: number, array: Ship[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: Ship, index: number, array: Ship[]) => unknown, thisArg?: any): boolean; }
2828
>this.ships : Ship[]
2929
>this : this
3030
>ships : Ship[]
31-
>every : (callbackfn: (value: Ship, index: number, array: Ship[]) => unknown, thisArg?: any) => boolean
31+
>every : { <S extends Ship>(callbackfn: (value: Ship, index: number, array: Ship[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: Ship, index: number, array: Ship[]) => unknown, thisArg?: any): boolean; }
3232
>function (val) { return val.isSunk; } : (val: Ship) => boolean
3333
>val : Ship
3434
>val.isSunk : boolean
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//// [arrayEvery.ts]
2+
const foo: (number | string)[] = ['aaa'];
3+
4+
const isString = (x: unknown): x is string => typeof x === 'string';
5+
6+
if (foo.every(isString)) {
7+
foo[0].slice(0);
8+
}
9+
10+
11+
//// [arrayEvery.js]
12+
var foo = ['aaa'];
13+
var isString = function (x) { return typeof x === 'string'; };
14+
if (foo.every(isString)) {
15+
foo[0].slice(0);
16+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/compiler/arrayEvery.ts ===
2+
const foo: (number | string)[] = ['aaa'];
3+
>foo : Symbol(foo, Decl(arrayEvery.ts, 0, 5))
4+
5+
const isString = (x: unknown): x is string => typeof x === 'string';
6+
>isString : Symbol(isString, Decl(arrayEvery.ts, 2, 5))
7+
>x : Symbol(x, Decl(arrayEvery.ts, 2, 18))
8+
>x : Symbol(x, Decl(arrayEvery.ts, 2, 18))
9+
>x : Symbol(x, Decl(arrayEvery.ts, 2, 18))
10+
11+
if (foo.every(isString)) {
12+
>foo.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
13+
>foo : Symbol(foo, Decl(arrayEvery.ts, 0, 5))
14+
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
15+
>isString : Symbol(isString, Decl(arrayEvery.ts, 2, 5))
16+
17+
foo[0].slice(0);
18+
>foo[0].slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --))
19+
>foo : Symbol(foo, Decl(arrayEvery.ts, 0, 5))
20+
>slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --))
21+
}
22+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/compiler/arrayEvery.ts ===
2+
const foo: (number | string)[] = ['aaa'];
3+
>foo : (string | number)[]
4+
>['aaa'] : string[]
5+
>'aaa' : "aaa"
6+
7+
const isString = (x: unknown): x is string => typeof x === 'string';
8+
>isString : (x: unknown) => x is string
9+
>(x: unknown): x is string => typeof x === 'string' : (x: unknown) => x is string
10+
>x : unknown
11+
>typeof x === 'string' : boolean
12+
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
13+
>x : unknown
14+
>'string' : "string"
15+
16+
if (foo.every(isString)) {
17+
>foo.every(isString) : boolean
18+
>foo.every : { <S extends string | number>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): boolean; }
19+
>foo : (string | number)[]
20+
>every : { <S extends string | number>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): boolean; }
21+
>isString : (x: unknown) => x is string
22+
23+
foo[0].slice(0);
24+
>foo[0].slice(0) : string
25+
>foo[0].slice : (start?: number, end?: number) => string
26+
>foo[0] : string
27+
>foo : string[]
28+
>0 : 0
29+
>slice : (start?: number, end?: number) => string
30+
>0 : 0
31+
}
32+

tests/baselines/reference/contextuallyTypedIife.symbols

Lines changed: 4 additions & 4 deletions
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.es5.d.ts, --, --))
50+
>numbers.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
5151
>numbers : Symbol(numbers, Decl(contextuallyTypedIife.ts, 17, 2))
52-
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
52+
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.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.es5.d.ts, --, --))
58+
>mixed.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
5959
>mixed : Symbol(mixed, Decl(contextuallyTypedIife.ts, 18, 2))
60-
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
60+
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@
105105
>(...numbers) => numbers.every(n => n > 0) : (numbers_0: number, numbers_1: number, numbers_2: number) => boolean
106106
>numbers : [number, number, number]
107107
>numbers.every(n => n > 0) : boolean
108-
>numbers.every : (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean
108+
>numbers.every : { <S extends number>(callbackfn: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }
109109
>numbers : [number, number, number]
110-
>every : (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean
110+
>every : { <S extends number>(callbackfn: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }
111111
>n => n > 0 : (n: number) => boolean
112112
>n : number
113113
>n > 0 : boolean
@@ -123,9 +123,9 @@
123123
>(...mixed) => mixed.every(n => !!n) : (mixed_0: number, mixed_1: string, mixed_2: string) => boolean
124124
>mixed : [number, string, string]
125125
>mixed.every(n => !!n) : boolean
126-
>mixed.every : (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any) => boolean
126+
>mixed.every : { <S extends string | number>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): boolean; }
127127
>mixed : [number, string, string]
128-
>every : (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any) => boolean
128+
>every : { <S extends string | number>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): boolean; }
129129
>n => !!n : (n: string | number) => boolean
130130
>n : string | number
131131
>!!n : boolean

tests/baselines/reference/contextuallyTypedIifeStrict.symbols

Lines changed: 4 additions & 4 deletions
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(contextuallyTypedIifeStrict.ts, 17, 2))
50-
>numbers.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
50+
>numbers.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
5151
>numbers : Symbol(numbers, Decl(contextuallyTypedIifeStrict.ts, 17, 2))
52-
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
52+
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
5353
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 17, 31))
5454
>n : Symbol(n, Decl(contextuallyTypedIifeStrict.ts, 17, 31))
5555

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

tests/baselines/reference/contextuallyTypedIifeStrict.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@
105105
>(...numbers) => numbers.every(n => n > 0) : (numbers_0: number, numbers_1: number, numbers_2: number) => boolean
106106
>numbers : [number, number, number]
107107
>numbers.every(n => n > 0) : boolean
108-
>numbers.every : (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean
108+
>numbers.every : { <S extends number>(callbackfn: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }
109109
>numbers : [number, number, number]
110-
>every : (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => boolean
110+
>every : { <S extends number>(callbackfn: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; }
111111
>n => n > 0 : (n: number) => boolean
112112
>n : number
113113
>n > 0 : boolean
@@ -123,9 +123,9 @@
123123
>(...mixed) => mixed.every(n => !!n) : (mixed_0: number, mixed_1: string, mixed_2: string) => boolean
124124
>mixed : [number, string, string]
125125
>mixed.every(n => !!n) : boolean
126-
>mixed.every : (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any) => boolean
126+
>mixed.every : { <S extends string | number>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): boolean; }
127127
>mixed : [number, string, string]
128-
>every : (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any) => boolean
128+
>every : { <S extends string | number>(callbackfn: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): boolean; }
129129
>n => !!n : (n: string | number) => boolean
130130
>n : string | number
131131
>!!n : boolean

tests/baselines/reference/destructuringParameterDeclaration4.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration4.ts(
4141
a1(...array2); // Error parameter type is (number|string)[]
4242
~~~~~~
4343
!!! error TS2552: Cannot find name 'array2'. Did you mean 'Array'?
44-
!!! related TS2728 /.ts/lib.es5.d.ts:1385:13: 'Array' is declared here.
44+
!!! related TS2728 /.ts/lib.es5.d.ts:1403:13: 'Array' is declared here.
4545
a5([1, 2, "string", false, true]); // Error, parameter type is [any, any, [[any]]]
4646
~~~~~~~~
4747
!!! error TS2322: Type 'string' is not assignable to type '[[any]]'.

tests/baselines/reference/destructuringTuple.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ tests/cases/compiler/destructuringTuple.ts(11,60): error TS2769: No overload mat
3535
!!! error TS2769: Type 'never[]' is not assignable to type '[]'.
3636
!!! error TS2769: Types of property 'length' are incompatible.
3737
!!! error TS2769: Type 'number' is not assignable to type '0'.
38-
!!! related TS6502 /.ts/lib.es5.d.ts:1350:24: The expected type comes from the return type of this signature.
39-
!!! related TS6502 /.ts/lib.es5.d.ts:1356:27: The expected type comes from the return type of this signature.
38+
!!! related TS6502 /.ts/lib.es5.d.ts:1368:24: The expected type comes from the return type of this signature.
39+
!!! related TS6502 /.ts/lib.es5.d.ts:1374:27: The expected type comes from the return type of this signature.
4040
~~
4141
!!! error TS2769: No overload matches this call.
4242
!!! error TS2769: Overload 1 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.

tests/baselines/reference/duplicateLocalVariable1.symbols

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ export class TestRunner {
3333
>arg2 : Symbol(arg2, Decl(duplicateLocalVariable1.ts, 15, 36))
3434

3535
return (arg1.every(function (val, index) { return val === arg2[index] }));
36-
>arg1.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
36+
>arg1.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
3737
>arg1 : Symbol(arg1, Decl(duplicateLocalVariable1.ts, 15, 24))
38-
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
38+
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
3939
>val : Symbol(val, Decl(duplicateLocalVariable1.ts, 16, 37))
4040
>index : Symbol(index, Decl(duplicateLocalVariable1.ts, 16, 41))
4141
>val : Symbol(val, Decl(duplicateLocalVariable1.ts, 16, 37))

tests/baselines/reference/duplicateLocalVariable1.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ export class TestRunner {
3636
return (arg1.every(function (val, index) { return val === arg2[index] }));
3737
>(arg1.every(function (val, index) { return val === arg2[index] })) : boolean
3838
>arg1.every(function (val, index) { return val === arg2[index] }) : boolean
39-
>arg1.every : (callbackfn: (value: any, index: number, array: any[]) => unknown, thisArg?: any) => boolean
39+
>arg1.every : { <S extends any>(callbackfn: (value: any, index: number, array: any[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean; }
4040
>arg1 : any[]
41-
>every : (callbackfn: (value: any, index: number, array: any[]) => unknown, thisArg?: any) => boolean
41+
>every : { <S extends any>(callbackfn: (value: any, index: number, array: any[]) => value is S, thisArg?: any): this is S[]; (callbackfn: (value: any, index: number, array: any[]) => unknown, thisArg?: any): boolean; }
4242
>function (val, index) { return val === arg2[index] } : (val: any, index: number) => boolean
4343
>val : any
4444
>index : number

0 commit comments

Comments
 (0)