Skip to content

Commit e1a72ff

Browse files
committed
new checker.ts and updated test results
1 parent 79f69d5 commit e1a72ff

File tree

4 files changed

+78
-9
lines changed

4 files changed

+78
-9
lines changed

src/compiler/checker.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34120,6 +34120,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3412034120
result = chooseOverload(candidates, assignableRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma);
3412134121
}
3412234122
if (result) {
34123+
const returnType = calculateSignatureReturnTypeForSpecialCases(result, args);
34124+
if (returnType) {
34125+
result = cloneSignature(result);
34126+
result.resolvedReturnType = returnType;
34127+
}
3412334128
return result;
3412434129
}
3412534130

@@ -34235,6 +34240,70 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3423534240

3423634241
return result;
3423734242

34243+
function calculateSignatureReturnTypeForSpecialCases(signature: Readonly<Signature>, args: readonly Expression[]): Type | undefined {
34244+
if (args.length >= 1) {
34245+
// In some tsx cases "symbol" is undefined, even though it is defined in the typechecker. So add ?
34246+
if (
34247+
signature.declaration?.symbol?.escapedName === "filter" && (
34248+
signature.declaration?.symbol?.parent?.escapedName === "Array"
34249+
|| signature.declaration?.symbol?.parent?.escapedName === "ReadonlyArray"
34250+
)
34251+
) {
34252+
const arg0Type = getTypeOfExpression(args[0]);
34253+
// This is safe even if a different BooleanConstructor is defined in a namespace,
34254+
// because in that case arg0Type.symbol.escapedName will appear as "__type".
34255+
if (arg0Type.symbol.escapedName === "BooleanConstructor") {
34256+
// It is a-priori knowledge the filter returns the same type as the array type
34257+
// for a signature succeeding when BooleanConstructor is the argument type
34258+
let returnType = (signature.mapper as undefined | { targets: readonly Type[]; })?.targets[1];
34259+
// result.declaration?.symbol.parent?.escapedName==="ReadonlyArray"
34260+
if (returnType) {
34261+
const nonFalsieArrayTypesOut: Type[] = [];
34262+
// the return type can only be an array type.
34263+
// It cant actually be a union of array types for a single signature.
34264+
// So this forEachType could be skipped, but may be used in the future with union of array types
34265+
forEachType(returnType, at => {
34266+
let elemType: Type;
34267+
if (isTupleType(at)) {
34268+
// The tuple elements are unionized, *abondoning* the tupleness becuase
34269+
// filtering could create result of varying length.
34270+
// For variable length tuples, undefined is *not* added to the union within getElementTypes.
34271+
elemType = getUnionType(getElementTypes(at));
34272+
}
34273+
else if (isTupleLikeType(at)) {
34274+
// doesn't handle tupleLikeTypes
34275+
// just return the orginal type
34276+
nonFalsieArrayTypesOut.push(at);
34277+
return;
34278+
}
34279+
else {
34280+
elemType = getElementTypeOfArrayType(at) || anyType; // need test case for anyType
34281+
}
34282+
const nonFalsieElemTypes: Type[] = [];
34283+
nonFalsieElemTypes.push(filterType(
34284+
elemType,
34285+
t => {
34286+
const facts = getTypeFacts(t, TypeFacts.Truthy | TypeFacts.Falsy);
34287+
if (facts === TypeFacts.Falsy) {
34288+
return false;
34289+
}
34290+
else {
34291+
return true;
34292+
}
34293+
},
34294+
));
34295+
// output arrays are not not readonly
34296+
const atout = createArrayType(getUnionType(nonFalsieElemTypes));
34297+
nonFalsieArrayTypesOut.push(atout);
34298+
});
34299+
returnType = getUnionType(nonFalsieArrayTypesOut);
34300+
return returnType;
34301+
}
34302+
}
34303+
}
34304+
}
34305+
}
34306+
3423834307
function addImplementationSuccessElaboration(failed: Signature, diagnostic: Diagnostic) {
3423934308
const oldCandidatesForArgumentError = candidatesForArgumentError;
3424034309
const oldCandidateForArgumentArityError = candidateForArgumentArityError;

tests/baselines/reference/arrayFilterBooleanOverload.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[]
2626

2727
//// [arrayFilterBooleanOverload.d.ts]
2828
declare const nullableValues: (string | null)[];
29-
declare const values1: (string | null)[];
29+
declare const values1: string[];
3030
declare const values2: (string | null)[];
3131
declare const arr: readonly [0, 1, "", "foo", null];
32-
declare const arr2: ("" | 0 | 1 | "foo" | null)[];
32+
declare const arr2: (1 | "foo")[];

tests/baselines/reference/arrayFilterBooleanOverload.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const nullableValues = ['a', 'b', null]; // expect (string | null)[]
88
>'b' : "b"
99

1010
const values1 = nullableValues.filter(Boolean); // expect string[]
11-
>values1 : (string | null)[]
12-
>nullableValues.filter(Boolean) : (string | null)[]
11+
>values1 : string[]
12+
>nullableValues.filter(Boolean) : string[]
1313
>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)[]; }
1414
>nullableValues : (string | null)[]
1515
>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)[]; }
@@ -35,8 +35,8 @@ const arr = [0, 1, "", "foo", null] as const;
3535
>"foo" : "foo"
3636

3737
const arr2 = arr.filter(Boolean); // expect ("foo" | 1)[]
38-
>arr2 : ("" | 0 | 1 | "foo" | null)[]
39-
>arr.filter(Boolean) : ("" | 0 | 1 | "foo" | null)[]
38+
>arr2 : (1 | "foo")[]
39+
>arr.filter(Boolean) : (1 | "foo")[]
4040
>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)[]; }
4141
>arr : readonly [0, 1, "", "foo", null]
4242
>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)[]; }

tests/baselines/reference/unionOfArraysBooleanFilterCall.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type Falsey = "" | 0 | false | null | undefined;
2121

2222

2323
([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[]
24-
>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz | Falsey)[]
24+
>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz)[]
2525
>([] 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)[]; }
2626
>([] as (Fizz|Falsey)[] | (Buzz|Falsey)[]) : (Fizz | Falsey)[] | (Buzz | Falsey)[]
2727
>[] as (Fizz|Falsey)[] | (Buzz|Falsey)[] : (Fizz | Falsey)[] | (Buzz | Falsey)[]
@@ -30,7 +30,7 @@ type Falsey = "" | 0 | false | null | undefined;
3030
>Boolean : BooleanConstructor
3131

3232
([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean); // expect type (Fizz|Buzz)[]
33-
>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz | Falsey)[]
33+
>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]).filter(Boolean) : (Fizz | Buzz)[]
3434
>([] 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)[]; }
3535
>([] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[]) : (Fizz | Falsey)[] | readonly (Buzz | Falsey)[]
3636
>[] as (Fizz|Falsey)[] | readonly (Buzz|Falsey)[] : (Fizz | Falsey)[] | readonly (Buzz | Falsey)[]
@@ -39,7 +39,7 @@ type Falsey = "" | 0 | false | null | undefined;
3939
>Boolean : BooleanConstructor
4040

4141
([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean); // expect type (Fizz|Buzz)[]
42-
>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean) : (Fizz | Buzz | Falsey)[]
42+
>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]).filter(Boolean) : (Fizz | Buzz)[]
4343
>([] 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)[]; }
4444
>([] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?]) : [Fizz | Falsey] | readonly [(Buzz | Falsey)?]
4545
>[] as [Fizz|Falsey] | readonly [(Buzz|Falsey)?] : [Fizz | Falsey] | readonly [(Buzz | Falsey)?]

0 commit comments

Comments
 (0)