From f56e8f0810b524e59869f4bb8f21f7b96b8a7a0e 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] new checker.ts and updated test results --- src/compiler/checker.ts | 67 +++++++++++++++++++ .../reference/arrayFilterBooleanOverload.js | 4 +- .../arrayFilterBooleanOverload.types | 8 +-- .../unionOfArraysBooleanFilterCall.types | 6 +- 4 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ac1038a4cdbe..8909773368adf 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,68 @@ 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)?]