Skip to content

Commit

Permalink
new checker.ts and updated test results
Browse files Browse the repository at this point in the history
  • Loading branch information
craigphicks committed Nov 9, 2023
1 parent 79f69d5 commit f56e8f0
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 9 deletions.
67 changes: 67 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -34235,6 +34240,68 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {

return result;

function calculateSignatureReturnTypeForSpecialCases(signature: Readonly<Signature>, 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;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayFilterBooleanOverload.js
Original file line number Diff line number Diff line change
Expand Up @@ -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")[];
8 changes: 4 additions & 4 deletions tests/baselines/reference/arrayFilterBooleanOverload.types
Original file line number Diff line number Diff line change
Expand Up @@ -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 : { <S extends string | null>(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 : { <S extends string | null>(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)[]; }
Expand All @@ -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 : { <S extends "" | 0 | 1 | "foo" | null>(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 : { <S extends "" | 0 | 1 | "foo" | null>(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)[]; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 : { <S extends Fizz | Falsey>(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)[]; } | { <S extends Buzz | 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)[]
Expand All @@ -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 : { <S extends Fizz | Falsey>(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)[]; } | { <S extends Buzz | 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)[]
Expand All @@ -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 : { <S extends Fizz | Falsey>(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)[]; } | { <S extends Buzz | 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)?]
Expand Down

0 comments on commit f56e8f0

Please sign in to comment.