Skip to content

Array predicates should not have to return boolean #27509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/lib/es2015.core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface Array<T> {
* predicate. If it is not provided, undefined is used instead.
*/
find<S extends T>(predicate: (this: void, value: T, index: number, obj: T[]) => value is S, thisArg?: any): S | undefined;
find(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): T | undefined;
find(predicate: (value: T, index: number, obj: T[]) => {} | null | undefined, thisArg?: any): T | undefined;

/**
* Returns the index of the first element in the array where predicate is true, and -1
Expand All @@ -20,7 +20,7 @@ interface Array<T> {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): number;
findIndex(predicate: (value: T, index: number, obj: T[]) => {} | null | undefined, thisArg?: any): number;

/**
* Returns the this object after filling the section identified by start and end with value
Expand Down Expand Up @@ -324,7 +324,7 @@ interface ReadonlyArray<T> {
* predicate. If it is not provided, undefined is used instead.
*/
find<S extends T>(predicate: (this: void, value: T, index: number, obj: ReadonlyArray<T>) => value is S, thisArg?: any): S | undefined;
find(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => boolean, thisArg?: any): T | undefined;
find(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => {} | null | undefined, thisArg?: any): T | undefined;

/**
* Returns the index of the first element in the array where predicate is true, and -1
Expand All @@ -335,7 +335,7 @@ interface ReadonlyArray<T> {
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => boolean, thisArg?: any): number;
findIndex(predicate: (value: T, index: number, obj: ReadonlyArray<T>) => {} | null | undefined, thisArg?: any): number;
}

interface RegExp {
Expand Down
80 changes: 40 additions & 40 deletions src/lib/es5.d.ts

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions tests/baselines/reference/2dArrays.types
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ class Board {

return this.ships.every(function (val) { return val.isSunk; });
>this.ships.every(function (val) { return val.isSunk; }) : boolean
>this.ships.every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
>this.ships.every : (callbackfn: (value: Ship, index: number, array: Ship[]) => {}, thisArg?: any) => boolean
>this.ships : Ship[]
>this : this
>ships : Ship[]
>every : (callbackfn: (value: Ship, index: number, array: Ship[]) => boolean, thisArg?: any) => boolean
>every : (callbackfn: (value: Ship, index: number, array: Ship[]) => {}, thisArg?: any) => boolean
>function (val) { return val.isSunk; } : (val: Ship) => boolean
>val : Ship
>val.isSunk : boolean
Expand Down
31 changes: 31 additions & 0 deletions tests/baselines/reference/arrayEvery.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
tests/cases/compiler/arrayEvery.ts(12,11): error TS2345: Argument of type '(x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, array: { name: string; }[]) => {}'.
Type 'void' is not assignable to type '{}'.
tests/cases/compiler/arrayEvery.ts(14,11): error TS2345: Argument of type '(x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, array: { name: string; }[]) => {}'.
Type 'void' is not assignable to type '{}'.


==== tests/cases/compiler/arrayEvery.ts (2 errors) ====
// Tests fix for #27496, predicates should not have to return booleans
const foo = [
{ name: 'bar' },
{ name: null },
{ name: 'baz' }
];
const fizz = [
{ name: 'buzz' },
{ name: 'fizzbuzz' }
];

foo.every(x => {});
~~~~~~~
!!! error TS2345: Argument of type '(x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, array: { name: string; }[]) => {}'.
!!! error TS2345: Type 'void' is not assignable to type '{}'.
foo.every(x => "");
foo.every(x => { return; });
~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '(x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, array: { name: string; }[]) => {}'.
!!! error TS2345: Type 'void' is not assignable to type '{}'.
foo.every(x => { return null; });
foo.every(x => { return undefined; });
foo.every(x => x.name);
fizz.every(x => x.name);
38 changes: 38 additions & 0 deletions tests/baselines/reference/arrayEvery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//// [arrayEvery.ts]
// Tests fix for #27496, predicates should not have to return booleans
const foo = [
{ name: 'bar' },
{ name: null },
{ name: 'baz' }
];
const fizz = [
{ name: 'buzz' },
{ name: 'fizzbuzz' }
];

foo.every(x => {});
foo.every(x => "");
foo.every(x => { return; });
foo.every(x => { return null; });
foo.every(x => { return undefined; });
foo.every(x => x.name);
fizz.every(x => x.name);

//// [arrayEvery.js]
// Tests fix for #27496, predicates should not have to return booleans
var foo = [
{ name: 'bar' },
{ name: null },
{ name: 'baz' }
];
var fizz = [
{ name: 'buzz' },
{ name: 'fizzbuzz' }
];
foo.every(function (x) { });
foo.every(function (x) { return ""; });
foo.every(function (x) { return; });
foo.every(function (x) { return null; });
foo.every(function (x) { return undefined; });
foo.every(function (x) { return x.name; });
fizz.every(function (x) { return x.name; });
75 changes: 75 additions & 0 deletions tests/baselines/reference/arrayEvery.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
=== tests/cases/compiler/arrayEvery.ts ===
// Tests fix for #27496, predicates should not have to return booleans
const foo = [
>foo : Symbol(foo, Decl(arrayEvery.ts, 1, 5))

{ name: 'bar' },
>name : Symbol(name, Decl(arrayEvery.ts, 2, 5))

{ name: null },
>name : Symbol(name, Decl(arrayEvery.ts, 3, 5))

{ name: 'baz' }
>name : Symbol(name, Decl(arrayEvery.ts, 4, 5))

];
const fizz = [
>fizz : Symbol(fizz, Decl(arrayEvery.ts, 6, 5))

{ name: 'buzz' },
>name : Symbol(name, Decl(arrayEvery.ts, 7, 5))

{ name: 'fizzbuzz' }
>name : Symbol(name, Decl(arrayEvery.ts, 8, 5))

];

foo.every(x => {});
>foo.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(arrayEvery.ts, 1, 5))
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(arrayEvery.ts, 11, 10))

foo.every(x => "");
>foo.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(arrayEvery.ts, 1, 5))
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(arrayEvery.ts, 12, 10))

foo.every(x => { return; });
>foo.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(arrayEvery.ts, 1, 5))
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(arrayEvery.ts, 13, 10))

foo.every(x => { return null; });
>foo.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(arrayEvery.ts, 1, 5))
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(arrayEvery.ts, 14, 10))

foo.every(x => { return undefined; });
>foo.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(arrayEvery.ts, 1, 5))
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(arrayEvery.ts, 15, 10))
>undefined : Symbol(undefined)

foo.every(x => x.name);
>foo.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(arrayEvery.ts, 1, 5))
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(arrayEvery.ts, 16, 10))
>x.name : Symbol(name, Decl(arrayEvery.ts, 2, 5))
>x : Symbol(x, Decl(arrayEvery.ts, 16, 10))
>name : Symbol(name, Decl(arrayEvery.ts, 2, 5))

fizz.every(x => x.name);
>fizz.every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>fizz : Symbol(fizz, Decl(arrayEvery.ts, 6, 5))
>every : Symbol(Array.every, Decl(lib.es5.d.ts, --, --))
>x : Symbol(x, Decl(arrayEvery.ts, 17, 11))
>x.name : Symbol(name, Decl(arrayEvery.ts, 7, 5))
>x : Symbol(x, Decl(arrayEvery.ts, 17, 11))
>name : Symbol(name, Decl(arrayEvery.ts, 7, 5))

103 changes: 103 additions & 0 deletions tests/baselines/reference/arrayEvery.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
=== tests/cases/compiler/arrayEvery.ts ===
// Tests fix for #27496, predicates should not have to return booleans
const foo = [
>foo : { name: string; }[]
>[ { name: 'bar' }, { name: null }, { name: 'baz' }] : { name: string; }[]

{ name: 'bar' },
>{ name: 'bar' } : { name: string; }
>name : string
>'bar' : "bar"

{ name: null },
>{ name: null } : { name: null; }
>name : null
>null : null

{ name: 'baz' }
>{ name: 'baz' } : { name: string; }
>name : string
>'baz' : "baz"

];
const fizz = [
>fizz : { name: string; }[]
>[ { name: 'buzz' }, { name: 'fizzbuzz' }] : { name: string; }[]

{ name: 'buzz' },
>{ name: 'buzz' } : { name: string; }
>name : string
>'buzz' : "buzz"

{ name: 'fizzbuzz' }
>{ name: 'fizzbuzz' } : { name: string; }
>name : string
>'fizzbuzz' : "fizzbuzz"

];

foo.every(x => {});
>foo.every(x => {}) : boolean
>foo.every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>foo : { name: string; }[]
>every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>x => {} : (x: { name: string; }) => void
>x : { name: string; }

foo.every(x => "");
>foo.every(x => "") : boolean
>foo.every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>foo : { name: string; }[]
>every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>x => "" : (x: { name: string; }) => string
>x : { name: string; }
>"" : ""

foo.every(x => { return; });
>foo.every(x => { return; }) : boolean
>foo.every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>foo : { name: string; }[]
>every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>x => { return; } : (x: { name: string; }) => void
>x : { name: string; }

foo.every(x => { return null; });
>foo.every(x => { return null; }) : boolean
>foo.every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>foo : { name: string; }[]
>every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>x => { return null; } : (x: { name: string; }) => any
>x : { name: string; }
>null : null

foo.every(x => { return undefined; });
>foo.every(x => { return undefined; }) : boolean
>foo.every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>foo : { name: string; }[]
>every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>x => { return undefined; } : (x: { name: string; }) => any
>x : { name: string; }
>undefined : undefined

foo.every(x => x.name);
>foo.every(x => x.name) : boolean
>foo.every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>foo : { name: string; }[]
>every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>x => x.name : (x: { name: string; }) => string
>x : { name: string; }
>x.name : string
>x : { name: string; }
>name : string

fizz.every(x => x.name);
>fizz.every(x => x.name) : boolean
>fizz.every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>fizz : { name: string; }[]
>every : (callbackfn: (value: { name: string; }, index: number, array: { name: string; }[]) => {}, thisArg?: any) => boolean
>x => x.name : (x: { name: string; }) => string
>x : { name: string; }
>x.name : string
>x : { name: string; }
>name : string

39 changes: 39 additions & 0 deletions tests/baselines/reference/arrayFind.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
tests/cases/compiler/arrayFind.ts(21,10): error TS2345: Argument of type '(this: void, x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, obj: { name: string; }[]) => {}'.
Type 'void' is not assignable to type '{}'.
tests/cases/compiler/arrayFind.ts(23,10): error TS2345: Argument of type '(this: void, x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, obj: { name: string; }[]) => {}'.
Type 'void' is not assignable to type '{}'.


==== tests/cases/compiler/arrayFind.ts (2 errors) ====
// test fix for #18112, type guard predicates should narrow returned element
function isNumber(x: any): x is number {
return typeof x === "number";
}

const arrayOfStringsNumbersAndBooleans = ["string", false, 0, "strung", 1, true];
const foundNumber: number | undefined = arrayOfStringsNumbersAndBooleans.find(isNumber);

const readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans as ReadonlyArray<string | number | boolean>;
const readonlyFoundNumber: number | undefined = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);



// Tests fix for #27496, predicates should not have to return booleans
const foo = [
{ name: 'bar' },
{ name: null },
{ name: 'baz' }
];

foo.find(x => {});
~~~~~~~
!!! error TS2345: Argument of type '(this: void, x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, obj: { name: string; }[]) => {}'.
!!! error TS2345: Type 'void' is not assignable to type '{}'.
foo.find(x => "");
foo.find(x => { return; });
~~~~~~~~~~~~~~~~
!!! error TS2345: Argument of type '(this: void, x: { name: string; }) => void' is not assignable to parameter of type '(value: { name: string; }, index: number, obj: { name: string; }[]) => {}'.
!!! error TS2345: Type 'void' is not assignable to type '{}'.
foo.find(x => { return null; });
foo.find(x => { return undefined; });
foo.find(x => x.name);
29 changes: 28 additions & 1 deletion tests/baselines/reference/arrayFind.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,22 @@ const foundNumber: number | undefined = arrayOfStringsNumbersAndBooleans.find(is

const readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans as ReadonlyArray<string | number | boolean>;
const readonlyFoundNumber: number | undefined = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);




// Tests fix for #27496, predicates should not have to return booleans
const foo = [
{ name: 'bar' },
{ name: null },
{ name: 'baz' }
];

foo.find(x => {});
foo.find(x => "");
foo.find(x => { return; });
foo.find(x => { return null; });
foo.find(x => { return undefined; });
foo.find(x => x.name);

//// [arrayFind.js]
// test fix for #18112, type guard predicates should narrow returned element
Expand All @@ -20,3 +35,15 @@ var arrayOfStringsNumbersAndBooleans = ["string", false, 0, "strung", 1, true];
var foundNumber = arrayOfStringsNumbersAndBooleans.find(isNumber);
var readonlyArrayOfStringsNumbersAndBooleans = arrayOfStringsNumbersAndBooleans;
var readonlyFoundNumber = readonlyArrayOfStringsNumbersAndBooleans.find(isNumber);
// Tests fix for #27496, predicates should not have to return booleans
var foo = [
{ name: 'bar' },
{ name: null },
{ name: 'baz' }
];
foo.find(function (x) { });
foo.find(function (x) { return ""; });
foo.find(function (x) { return; });
foo.find(function (x) { return null; });
foo.find(function (x) { return undefined; });
foo.find(function (x) { return x.name; });
Loading