From 79f69d526059a3f3396865198ec84ca6126d5e21 Mon Sep 17 00:00:00 2001 From: craigphicks <13205732+craigphicks@users.noreply.github.com> Date: Thu, 9 Nov 2023 21:09:00 +0000 Subject: [PATCH 1/3] original checker and new tests --- .../reference/arrayFilterBooleanOverload.js | 32 ++++++ .../arrayFilterBooleanOverload.symbols | 33 +++++++ .../arrayFilterBooleanOverload.types | 45 +++++++++ .../unionOfArraysBooleanFilterCall.js | 38 ++++++++ .../unionOfArraysBooleanFilterCall.symbols | 97 +++++++++++++++++++ .../unionOfArraysBooleanFilterCall.types | 95 ++++++++++++++++++ .../compiler/arrayFilterBooleanOverload.ts | 15 +++ .../unionOfArraysBooleanFilterCall.ts | 27 ++++++ 8 files changed, 382 insertions(+) create mode 100644 tests/baselines/reference/arrayFilterBooleanOverload.js create mode 100644 tests/baselines/reference/arrayFilterBooleanOverload.symbols create mode 100644 tests/baselines/reference/arrayFilterBooleanOverload.types create mode 100644 tests/baselines/reference/unionOfArraysBooleanFilterCall.js create mode 100644 tests/baselines/reference/unionOfArraysBooleanFilterCall.symbols create mode 100644 tests/baselines/reference/unionOfArraysBooleanFilterCall.types create mode 100644 tests/cases/compiler/arrayFilterBooleanOverload.ts create mode 100644 tests/cases/compiler/unionOfArraysBooleanFilterCall.ts diff --git a/tests/baselines/reference/arrayFilterBooleanOverload.js b/tests/baselines/reference/arrayFilterBooleanOverload.js new file mode 100644 index 0000000000000..ab2385ba9963d --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanOverload.js @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/arrayFilterBooleanOverload.ts] //// + +//// [arrayFilterBooleanOverload.ts] +const nullableValues = ['a', 'b', null]; // expect (string | null)[] + +const values1 = nullableValues.filter(Boolean); // expect string[] + +// @ts-expect-error +const values2 = nullableValues.filter(new Boolean); + +const arr = [0, 1, "", "foo", null] as const; + +const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[] + + + +//// [arrayFilterBooleanOverload.js] +"use strict"; +const nullableValues = ['a', 'b', null]; // expect (string | null)[] +const values1 = nullableValues.filter(Boolean); // expect string[] +// @ts-expect-error +const values2 = nullableValues.filter(new Boolean); +const arr = [0, 1, "", "foo", null]; +const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[] + + +//// [arrayFilterBooleanOverload.d.ts] +declare const nullableValues: (string | null)[]; +declare const values1: (string | null)[]; +declare const values2: (string | null)[]; +declare const arr: readonly [0, 1, "", "foo", null]; +declare const arr2: ("" | 0 | 1 | "foo" | null)[]; diff --git a/tests/baselines/reference/arrayFilterBooleanOverload.symbols b/tests/baselines/reference/arrayFilterBooleanOverload.symbols new file mode 100644 index 0000000000000..611ed5a8a7b94 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanOverload.symbols @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/arrayFilterBooleanOverload.ts] //// + +=== arrayFilterBooleanOverload.ts === +const nullableValues = ['a', 'b', null]; // expect (string | null)[] +>nullableValues : Symbol(nullableValues, Decl(arrayFilterBooleanOverload.ts, 0, 5)) + +const values1 = nullableValues.filter(Boolean); // expect string[] +>values1 : Symbol(values1, Decl(arrayFilterBooleanOverload.ts, 2, 5)) +>nullableValues.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>nullableValues : Symbol(nullableValues, Decl(arrayFilterBooleanOverload.ts, 0, 5)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +// @ts-expect-error +const values2 = nullableValues.filter(new Boolean); +>values2 : Symbol(values2, Decl(arrayFilterBooleanOverload.ts, 5, 5)) +>nullableValues.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>nullableValues : Symbol(nullableValues, Decl(arrayFilterBooleanOverload.ts, 0, 5)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +const arr = [0, 1, "", "foo", null] as const; +>arr : Symbol(arr, Decl(arrayFilterBooleanOverload.ts, 7, 5)) +>const : Symbol(const) + +const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[] +>arr2 : Symbol(arr2, Decl(arrayFilterBooleanOverload.ts, 9, 5)) +>arr.filter : Symbol(ReadonlyArray.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(arrayFilterBooleanOverload.ts, 7, 5)) +>filter : Symbol(ReadonlyArray.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + diff --git a/tests/baselines/reference/arrayFilterBooleanOverload.types b/tests/baselines/reference/arrayFilterBooleanOverload.types new file mode 100644 index 0000000000000..57768d1f5fd7a --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanOverload.types @@ -0,0 +1,45 @@ +//// [tests/cases/compiler/arrayFilterBooleanOverload.ts] //// + +=== arrayFilterBooleanOverload.ts === +const nullableValues = ['a', 'b', null]; // expect (string | null)[] +>nullableValues : (string | null)[] +>['a', 'b', null] : (string | null)[] +>'a' : "a" +>'b' : "b" + +const values1 = nullableValues.filter(Boolean); // expect string[] +>values1 : (string | null)[] +>nullableValues.filter(Boolean) : (string | null)[] +>nullableValues.filter : { (predicate: (value: string | null, index: number, array: (string | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | null, index: number, array: (string | null)[]) => unknown, thisArg?: any): (string | null)[]; } +>nullableValues : (string | null)[] +>filter : { (predicate: (value: string | null, index: number, array: (string | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | null, index: number, array: (string | null)[]) => unknown, thisArg?: any): (string | null)[]; } +>Boolean : BooleanConstructor + +// @ts-expect-error +const values2 = nullableValues.filter(new Boolean); +>values2 : (string | null)[] +>nullableValues.filter(new Boolean) : (string | null)[] +>nullableValues.filter : { (predicate: (value: string | null, index: number, array: (string | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | null, index: number, array: (string | null)[]) => unknown, thisArg?: any): (string | null)[]; } +>nullableValues : (string | null)[] +>filter : { (predicate: (value: string | null, index: number, array: (string | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | null, index: number, array: (string | null)[]) => unknown, thisArg?: any): (string | null)[]; } +>new Boolean : Boolean +>Boolean : BooleanConstructor + +const arr = [0, 1, "", "foo", null] as const; +>arr : readonly [0, 1, "", "foo", null] +>[0, 1, "", "foo", null] as const : readonly [0, 1, "", "foo", null] +>[0, 1, "", "foo", null] : readonly [0, 1, "", "foo", null] +>0 : 0 +>1 : 1 +>"" : "" +>"foo" : "foo" + +const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[] +>arr2 : ("" | 0 | 1 | "foo" | null)[] +>arr.filter(Boolean) : ("" | 0 | 1 | "foo" | null)[] +>arr.filter : { (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => unknown, thisArg?: any): ("" | 0 | 1 | "foo" | null)[]; } +>arr : readonly [0, 1, "", "foo", null] +>filter : { (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => unknown, thisArg?: any): ("" | 0 | 1 | "foo" | null)[]; } +>Boolean : BooleanConstructor + + diff --git a/tests/baselines/reference/unionOfArraysBooleanFilterCall.js b/tests/baselines/reference/unionOfArraysBooleanFilterCall.js new file mode 100644 index 0000000000000..bad8408e74d5b --- /dev/null +++ b/tests/baselines/reference/unionOfArraysBooleanFilterCall.js @@ -0,0 +1,38 @@ +//// [tests/cases/compiler/unionOfArraysBooleanFilterCall.ts] //// + +//// [unionOfArraysBooleanFilterCall.ts] +interface Fizz { + id: number; + member: number; +} +interface Buzz { + id: number; + member: string; +} +type Falsey = "" | 0 | false | null | undefined; + + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] + +([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] + +([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean); // expect type (Fizz|Buzz)[] + +// confirm that the other filter signatures are still available and working + +declare function isFizz(x: unknown): x is Fizz; +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isFizz); // expect type Fizz[] + +declare function isBuzz(x: unknown): x is Buzz; +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isBuzz); // expect type Buzz[] + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(x => x && typeof x.member === "number"); // expect type (Fizz|Buzz|Falsey)[] + + +//// [unionOfArraysBooleanFilterCall.js] +[].filter(Boolean); // expect type (Fizz|Buzz)[] +[].filter(Boolean); // expect type (Fizz|Buzz)[] +[].filter(Boolean); // expect type (Fizz|Buzz)[] +[].filter(isFizz); // expect type Fizz[] +[].filter(isBuzz); // expect type Buzz[] +[].filter(x => x && typeof x.member === "number"); // expect type (Fizz|Buzz|Falsey)[] diff --git a/tests/baselines/reference/unionOfArraysBooleanFilterCall.symbols b/tests/baselines/reference/unionOfArraysBooleanFilterCall.symbols new file mode 100644 index 0000000000000..ca3964dc74977 --- /dev/null +++ b/tests/baselines/reference/unionOfArraysBooleanFilterCall.symbols @@ -0,0 +1,97 @@ +//// [tests/cases/compiler/unionOfArraysBooleanFilterCall.ts] //// + +=== unionOfArraysBooleanFilterCall.ts === +interface Fizz { +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) + + id: number; +>id : Symbol(Fizz.id, Decl(unionOfArraysBooleanFilterCall.ts, 0, 16)) + + member: number; +>member : Symbol(Fizz.member, Decl(unionOfArraysBooleanFilterCall.ts, 1, 15)) +} +interface Buzz { +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) + + id: number; +>id : Symbol(Buzz.id, Decl(unionOfArraysBooleanFilterCall.ts, 4, 16)) + + member: string; +>member : Symbol(Buzz.member, Decl(unionOfArraysBooleanFilterCall.ts, 5, 15)) +} +type Falsey = "" | 0 | false | null | undefined; +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) + + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] +>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter : Symbol(filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>filter : Symbol(filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean); // expect type (Fizz|Buzz)[] +>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter : Symbol(filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>filter : Symbol(filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +// confirm that the other filter signatures are still available and working + +declare function isFizz(x: unknown): x is Fizz; +>isFizz : Symbol(isFizz, Decl(unionOfArraysBooleanFilterCall.ts, 15, 66)) +>x : Symbol(x, Decl(unionOfArraysBooleanFilterCall.ts, 19, 24)) +>x : Symbol(x, Decl(unionOfArraysBooleanFilterCall.ts, 19, 24)) +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isFizz); // expect type Fizz[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isFizz : Symbol(isFizz, Decl(unionOfArraysBooleanFilterCall.ts, 15, 66)) + +declare function isBuzz(x: unknown): x is Buzz; +>isBuzz : Symbol(isBuzz, Decl(unionOfArraysBooleanFilterCall.ts, 20, 57)) +>x : Symbol(x, Decl(unionOfArraysBooleanFilterCall.ts, 22, 24)) +>x : Symbol(x, Decl(unionOfArraysBooleanFilterCall.ts, 22, 24)) +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isBuzz); // expect type Buzz[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>isBuzz : Symbol(isBuzz, Decl(unionOfArraysBooleanFilterCall.ts, 20, 57)) + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(x => x && typeof x.member === "number"); // expect type (Fizz|Buzz|Falsey)[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Fizz : Symbol(Fizz, Decl(unionOfArraysBooleanFilterCall.ts, 0, 0)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>Buzz : Symbol(Buzz, Decl(unionOfArraysBooleanFilterCall.ts, 3, 1)) +>Falsey : Symbol(Falsey, Decl(unionOfArraysBooleanFilterCall.ts, 7, 1)) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(unionOfArraysBooleanFilterCall.ts, 25, 49)) +>x : Symbol(x, Decl(unionOfArraysBooleanFilterCall.ts, 25, 49)) +>x.member : Symbol(member, Decl(unionOfArraysBooleanFilterCall.ts, 1, 15), Decl(unionOfArraysBooleanFilterCall.ts, 5, 15)) +>x : Symbol(x, Decl(unionOfArraysBooleanFilterCall.ts, 25, 49)) +>member : Symbol(member, Decl(unionOfArraysBooleanFilterCall.ts, 1, 15), Decl(unionOfArraysBooleanFilterCall.ts, 5, 15)) + diff --git a/tests/baselines/reference/unionOfArraysBooleanFilterCall.types b/tests/baselines/reference/unionOfArraysBooleanFilterCall.types new file mode 100644 index 0000000000000..5d02091fb14fc --- /dev/null +++ b/tests/baselines/reference/unionOfArraysBooleanFilterCall.types @@ -0,0 +1,95 @@ +//// [tests/cases/compiler/unionOfArraysBooleanFilterCall.ts] //// + +=== unionOfArraysBooleanFilterCall.ts === +interface Fizz { + id: number; +>id : number + + member: number; +>member : number +} +interface Buzz { + id: number; +>id : number + + member: string; +>member : string +} +type Falsey = "" | 0 | false | null | undefined; +>Falsey : false | "" | 0 +>false : false + + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz | Falsey)[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]) : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] as (Fizz|Falsey)[] | (Buzz|Falsey)[] : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] : undefined[] +>filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>Boolean : BooleanConstructor + +([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] +>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz | Falsey)[] +>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]) : (Fizz | Falsey)[] | readonly (Buzz | Falsey)[] +>[] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[] : (Fizz | Falsey)[] | readonly (Buzz | Falsey)[] +>[] : undefined[] +>filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>Boolean : BooleanConstructor + +([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean); // expect type (Fizz|Buzz)[] +>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean) : (Fizz | Buzz | Falsey)[] +>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]) : [Fizz | Falsey] | readonly [(Buzz | Falsey)?] +>[] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?] : [Fizz | Falsey] | readonly [(Buzz | Falsey)?] +>[] : [] +>filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>Boolean : BooleanConstructor + +// confirm that the other filter signatures are still available and working + +declare function isFizz(x: unknown): x is Fizz; +>isFizz : (x: unknown) => x is Fizz +>x : unknown + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isFizz); // expect type Fizz[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isFizz) : Fizz[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]) : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] as (Fizz|Falsey)[] | (Buzz|Falsey)[] : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] : undefined[] +>filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>isFizz : (x: unknown) => x is Fizz + +declare function isBuzz(x: unknown): x is Buzz; +>isBuzz : (x: unknown) => x is Buzz +>x : unknown + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isBuzz); // expect type Buzz[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isBuzz) : Buzz[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]) : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] as (Fizz|Falsey)[] | (Buzz|Falsey)[] : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] : undefined[] +>filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>isBuzz : (x: unknown) => x is Buzz + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(x => x && typeof x.member === "number"); // expect type (Fizz|Buzz|Falsey)[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(x => x && typeof x.member === "number") : (Fizz | Buzz | Falsey)[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]) : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] as (Fizz|Falsey)[] | (Buzz|Falsey)[] : (Fizz | Falsey)[] | (Buzz | Falsey)[] +>[] : undefined[] +>filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } +>x => x && typeof x.member === "number" : (x: Fizz | Buzz | Falsey) => boolean +>x : Fizz | Buzz | Falsey +>x && typeof x.member === "number" : boolean +>x : Fizz | Buzz | Falsey +>typeof x.member === "number" : boolean +>typeof x.member : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x.member : string | number +>x : Fizz | Buzz +>member : string | number +>"number" : "number" + diff --git a/tests/cases/compiler/arrayFilterBooleanOverload.ts b/tests/cases/compiler/arrayFilterBooleanOverload.ts new file mode 100644 index 0000000000000..a9f4faa9c0538 --- /dev/null +++ b/tests/cases/compiler/arrayFilterBooleanOverload.ts @@ -0,0 +1,15 @@ +// @strict: true +// @declaration: true +// @target: es6 + +const nullableValues = ['a', 'b', null]; // expect (string | null)[] + +const values1 = nullableValues.filter(Boolean); // expect string[] + +// @ts-expect-error +const values2 = nullableValues.filter(new Boolean); + +const arr = [0, 1, "", "foo", null] as const; + +const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[] + diff --git a/tests/cases/compiler/unionOfArraysBooleanFilterCall.ts b/tests/cases/compiler/unionOfArraysBooleanFilterCall.ts new file mode 100644 index 0000000000000..2e2de7a299558 --- /dev/null +++ b/tests/cases/compiler/unionOfArraysBooleanFilterCall.ts @@ -0,0 +1,27 @@ +// @target: es6 +interface Fizz { + id: number; + member: number; +} +interface Buzz { + id: number; + member: string; +} +type Falsey = "" | 0 | false | null | undefined; + + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] + +([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] + +([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean); // expect type (Fizz|Buzz)[] + +// confirm that the other filter signatures are still available and working + +declare function isFizz(x: unknown): x is Fizz; +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isFizz); // expect type Fizz[] + +declare function isBuzz(x: unknown): x is Buzz; +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(isBuzz); // expect type Buzz[] + +([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(x => x && typeof x.member === "number"); // expect type (Fizz|Buzz|Falsey)[] From e1a72ff670963027d4d410e8c0e3fad6e71f8ce2 Mon Sep 17 00:00:00 2001 From: craigphicks <13205732+craigphicks@users.noreply.github.com> Date: Thu, 9 Nov 2023 21:21:34 +0000 Subject: [PATCH 2/3] new checker.ts and updated test results --- src/compiler/checker.ts | 69 +++++++++++++++++++ .../reference/arrayFilterBooleanOverload.js | 4 +- .../arrayFilterBooleanOverload.types | 8 +-- .../unionOfArraysBooleanFilterCall.types | 6 +- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ac1038a4cdbe..26b26b0cc82bd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34120,6 +34120,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result = chooseOverload(candidates, assignableRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma); } if (result) { + const returnType = calculateSignatureReturnTypeForSpecialCases(result, args); + if (returnType) { + result = cloneSignature(result); + result.resolvedReturnType = returnType; + } return result; } @@ -34235,6 +34240,70 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; + function calculateSignatureReturnTypeForSpecialCases(signature: Readonly, args: readonly Expression[]): Type | undefined { + if (args.length >= 1) { + // In some tsx cases "symbol" is undefined, even though it is defined in the typechecker. So add ? + if ( + signature.declaration?.symbol?.escapedName === "filter" && ( + signature.declaration?.symbol?.parent?.escapedName === "Array" + || signature.declaration?.symbol?.parent?.escapedName === "ReadonlyArray" + ) + ) { + const arg0Type = getTypeOfExpression(args[0]); + // This is safe even if a different BooleanConstructor is defined in a namespace, + // because in that case arg0Type.symbol.escapedName will appear as "__type". + if (arg0Type.symbol.escapedName === "BooleanConstructor") { + // It is a-priori knowledge the filter returns the same type as the array type + // for a signature succeeding when BooleanConstructor is the argument type + let returnType = (signature.mapper as undefined | { targets: readonly Type[]; })?.targets[1]; + // result.declaration?.symbol.parent?.escapedName==="ReadonlyArray" + if (returnType) { + const nonFalsieArrayTypesOut: Type[] = []; + // the return type can only be an array type. + // It cant actually be a union of array types for a single signature. + // So this forEachType could be skipped, but may be used in the future with union of array types + forEachType(returnType, at => { + let elemType: Type; + if (isTupleType(at)) { + // The tuple elements are unionized, *abondoning* the tupleness becuase + // filtering could create result of varying length. + // For variable length tuples, undefined is *not* added to the union within getElementTypes. + elemType = getUnionType(getElementTypes(at)); + } + else if (isTupleLikeType(at)) { + // doesn't handle tupleLikeTypes + // just return the orginal type + nonFalsieArrayTypesOut.push(at); + return; + } + else { + elemType = getElementTypeOfArrayType(at) || anyType; // need test case for anyType + } + const nonFalsieElemTypes: Type[] = []; + nonFalsieElemTypes.push(filterType( + elemType, + t => { + const facts = getTypeFacts(t, TypeFacts.Truthy | TypeFacts.Falsy); + if (facts === TypeFacts.Falsy) { + return false; + } + else { + return true; + } + }, + )); + // output arrays are not not readonly + const atout = createArrayType(getUnionType(nonFalsieElemTypes)); + nonFalsieArrayTypesOut.push(atout); + }); + returnType = getUnionType(nonFalsieArrayTypesOut); + return returnType; + } + } + } + } + } + function addImplementationSuccessElaboration(failed: Signature, diagnostic: Diagnostic) { const oldCandidatesForArgumentError = candidatesForArgumentError; const oldCandidateForArgumentArityError = candidateForArgumentArityError; diff --git a/tests/baselines/reference/arrayFilterBooleanOverload.js b/tests/baselines/reference/arrayFilterBooleanOverload.js index ab2385ba9963d..ace5769bd8c9b 100644 --- a/tests/baselines/reference/arrayFilterBooleanOverload.js +++ b/tests/baselines/reference/arrayFilterBooleanOverload.js @@ -26,7 +26,7 @@ const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[] //// [arrayFilterBooleanOverload.d.ts] declare const nullableValues: (string | null)[]; -declare const values1: (string | null)[]; +declare const values1: string[]; declare const values2: (string | null)[]; declare const arr: readonly [0, 1, "", "foo", null]; -declare const arr2: ("" | 0 | 1 | "foo" | null)[]; +declare const arr2: (1 | "foo")[]; diff --git a/tests/baselines/reference/arrayFilterBooleanOverload.types b/tests/baselines/reference/arrayFilterBooleanOverload.types index 57768d1f5fd7a..03af0c6c60080 100644 --- a/tests/baselines/reference/arrayFilterBooleanOverload.types +++ b/tests/baselines/reference/arrayFilterBooleanOverload.types @@ -8,8 +8,8 @@ const nullableValues = ['a', 'b', null]; // expect (string | null)[] >'b' : "b" const values1 = nullableValues.filter(Boolean); // expect string[] ->values1 : (string | null)[] ->nullableValues.filter(Boolean) : (string | null)[] +>values1 : string[] +>nullableValues.filter(Boolean) : string[] >nullableValues.filter : { (predicate: (value: string | null, index: number, array: (string | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | null, index: number, array: (string | null)[]) => unknown, thisArg?: any): (string | null)[]; } >nullableValues : (string | null)[] >filter : { (predicate: (value: string | null, index: number, array: (string | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | null, index: number, array: (string | null)[]) => unknown, thisArg?: any): (string | null)[]; } @@ -35,8 +35,8 @@ const arr = [0, 1, "", "foo", null] as const; >"foo" : "foo" const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[] ->arr2 : ("" | 0 | 1 | "foo" | null)[] ->arr.filter(Boolean) : ("" | 0 | 1 | "foo" | null)[] +>arr2 : (1 | "foo")[] +>arr.filter(Boolean) : (1 | "foo")[] >arr.filter : { (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => unknown, thisArg?: any): ("" | 0 | 1 | "foo" | null)[]; } >arr : readonly [0, 1, "", "foo", null] >filter : { (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: "" | 0 | 1 | "foo" | null, index: number, array: readonly ("" | 0 | 1 | "foo" | null)[]) => unknown, thisArg?: any): ("" | 0 | 1 | "foo" | null)[]; } diff --git a/tests/baselines/reference/unionOfArraysBooleanFilterCall.types b/tests/baselines/reference/unionOfArraysBooleanFilterCall.types index 5d02091fb14fc..b2fcf008e6071 100644 --- a/tests/baselines/reference/unionOfArraysBooleanFilterCall.types +++ b/tests/baselines/reference/unionOfArraysBooleanFilterCall.types @@ -21,7 +21,7 @@ type Falsey = "" | 0 | false | null | undefined; ([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] ->([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz | Falsey)[] +>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz)[] >([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } >([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]) : (Fizz | Falsey)[] | (Buzz | Falsey)[] >[] as (Fizz|Falsey)[] | (Buzz|Falsey)[] : (Fizz | Falsey)[] | (Buzz | Falsey)[] @@ -30,7 +30,7 @@ type Falsey = "" | 0 | false | null | undefined; >Boolean : BooleanConstructor ([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[] ->([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz | Falsey)[] +>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz)[] >([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } >([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]) : (Fizz | Falsey)[] | readonly (Buzz | Falsey)[] >[] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[] : (Fizz | Falsey)[] | readonly (Buzz | Falsey)[] @@ -39,7 +39,7 @@ type Falsey = "" | 0 | false | null | undefined; >Boolean : BooleanConstructor ([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean); // expect type (Fizz|Buzz)[] ->([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean) : (Fizz | Buzz | Falsey)[] +>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean) : (Fizz | Buzz)[] >([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter : { (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Fizz | Falsey, index: number, array: (Fizz | Falsey)[]) => unknown, thisArg?: any): (Fizz | Falsey)[]; } | { (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Buzz | Falsey, index: number, array: readonly (Buzz | Falsey)[]) => unknown, thisArg?: any): (Buzz | Falsey)[]; } >([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]) : [Fizz | Falsey] | readonly [(Buzz | Falsey)?] >[] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?] : [Fizz | Falsey] | readonly [(Buzz | Falsey)?] From 7afbdfa33b045d699951cf7ef4531061eea02bdb Mon Sep 17 00:00:00 2001 From: craigphicks <13205732+craigphicks@users.noreply.github.com> Date: Thu, 9 Nov 2023 23:13:42 +0000 Subject: [PATCH 3/3] add ? after symbol --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 26b26b0cc82bd..74e20297b62f6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34252,7 +34252,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const arg0Type = getTypeOfExpression(args[0]); // This is safe even if a different BooleanConstructor is defined in a namespace, // because in that case arg0Type.symbol.escapedName will appear as "__type". - if (arg0Type.symbol.escapedName === "BooleanConstructor") { + if (arg0Type.symbol?.escapedName === "BooleanConstructor") { // It is a-priori knowledge the filter returns the same type as the array type // for a signature succeeding when BooleanConstructor is the argument type let returnType = (signature.mapper as undefined | { targets: readonly Type[]; })?.targets[1];