@@ -34120,6 +34120,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
34120
34120
result = chooseOverload(candidates, assignableRelation, isSingleNonGenericCandidate, signatureHelpTrailingComma);
34121
34121
}
34122
34122
if (result) {
34123
+ const returnType = calculateSignatureReturnTypeForSpecialCases(result, args);
34124
+ if (returnType) {
34125
+ result = cloneSignature(result);
34126
+ result.resolvedReturnType = returnType;
34127
+ }
34123
34128
return result;
34124
34129
}
34125
34130
@@ -34235,6 +34240,70 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
34235
34240
34236
34241
return result;
34237
34242
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
+
34238
34307
function addImplementationSuccessElaboration(failed: Signature, diagnostic: Diagnostic) {
34239
34308
const oldCandidatesForArgumentError = candidatesForArgumentError;
34240
34309
const oldCandidateForArgumentArityError = candidateForArgumentArityError;
0 commit comments