diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4a294a2474f26..abd6d9f07f809 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1487,6 +1487,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var lastGetCombinedModifierFlagsNode: Declaration | undefined; var lastGetCombinedModifierFlagsResult = ModifierFlags.None; + var chooseOverloadRecursionLevel = -1; + var chooseOverloadFlushNodesSignaturesReq: (Set | undefined)[] = []; + // for public members that accept a Node or one of its subtypes, we must guard against // synthetic nodes created during transformations by calling `getParseTreeNode`. // for most of these, we perform the guard only on `checker` to avoid any possible @@ -34310,69 +34313,55 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function chooseOverload(candidates: Signature[], relation: Map, isSingleNonGenericCandidate: boolean, signatureHelpTrailingComma = false) { - candidatesForArgumentError = undefined; - candidateForArgumentArityError = undefined; - candidateForTypeArgumentError = undefined; + chooseOverloadRecursionLevel++; + chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel] = undefined; + const result = chooseOverloadWorker(); + chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel] = undefined; + chooseOverloadRecursionLevel--; + return result; - if (isSingleNonGenericCandidate) { - const candidate = candidates[0]; - if (some(typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { - return undefined; - } - if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { - candidatesForArgumentError = [candidate]; - return undefined; - } - return candidate; - } + function chooseOverloadWorker() { + candidatesForArgumentError = undefined; + candidateForArgumentArityError = undefined; + candidateForTypeArgumentError = undefined; - for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) { - const candidate = candidates[candidateIndex]; - if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { - continue; + if (isSingleNonGenericCandidate) { + const candidate = candidates[0]; + if (some(typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { + return undefined; + } + if (getSignatureApplicabilityError(node, args, candidate, relation, CheckMode.Normal, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { + candidatesForArgumentError = [candidate]; + return undefined; + } + return candidate; } - let checkCandidate: Signature; - let inferenceContext: InferenceContext | undefined; - - if (candidate.typeParameters) { - let typeArgumentTypes: Type[] | undefined; - if (some(typeArguments)) { - typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false); - if (!typeArgumentTypes) { - candidateForTypeArgumentError = candidate; - continue; - } - } - else { - inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext); - argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal; - } - checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); - // If the original signature has a generic rest type, instantiation may produce a - // signature with different arity and we need to perform another arity check. - if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) { - candidateForArgumentArityError = checkCandidate; + for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) { + if (candidateIndex > 0) chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel] = new Set(); + const candidate = candidates[candidateIndex]; + if (!hasCorrectTypeArgumentArity(candidate, typeArguments) || !hasCorrectArity(node, args, candidate, signatureHelpTrailingComma)) { continue; } - } - else { - checkCandidate = candidate; - } - if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { - // Give preference to error candidates that have no rest parameters (as they are more specific) - (candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate); - continue; - } - if (argCheckMode) { - // If one or more context sensitive arguments were excluded, we start including - // them now (and keeping do so for any subsequent candidates) and perform a second - // round of type inference and applicability checking for this particular candidate. - argCheckMode = CheckMode.Normal; - if (inferenceContext) { - const typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext); - checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext.inferredTypeParameters); + + let checkCandidate: Signature; + let inferenceContext: InferenceContext | undefined; + + if (candidate.typeParameters) { + let typeArgumentTypes: Type[] | undefined; + if (some(typeArguments)) { + typeArgumentTypes = checkTypeArguments(candidate, typeArguments, /*reportErrors*/ false); + if (!typeArgumentTypes) { + candidateForTypeArgumentError = candidate; + continue; + } + } + else { + inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); + typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode | CheckMode.SkipGenericFunctions, inferenceContext); + argCheckMode |= inferenceContext.flags & InferenceFlags.SkippedGenericFunction ? CheckMode.SkipGenericFunctions : CheckMode.Normal; + } + checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); // If the original signature has a generic rest type, instantiation may produce a // signature with different arity and we need to perform another arity check. if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) { @@ -34380,17 +34369,41 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { continue; } } + else { + checkCandidate = candidate; + } if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { // Give preference to error candidates that have no rest parameters (as they are more specific) (candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate); continue; } + if (argCheckMode) { + // If one or more context sensitive arguments were excluded, we start including + // them now (and keeping do so for any subsequent candidates) and perform a second + // round of type inference and applicability checking for this particular candidate. + argCheckMode = CheckMode.Normal; + if (inferenceContext) { + const typeArgumentTypes = inferTypeArguments(node, candidate, args, argCheckMode, inferenceContext); + checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext.inferredTypeParameters); + // If the original signature has a generic rest type, instantiation may produce a + // signature with different arity and we need to perform another arity check. + if (getNonArrayRestType(candidate) && !hasCorrectArity(node, args, checkCandidate, signatureHelpTrailingComma)) { + candidateForArgumentArityError = checkCandidate; + continue; + } + } + if (getSignatureApplicabilityError(node, args, checkCandidate, relation, argCheckMode, /*reportErrors*/ false, /*containingMessageChain*/ undefined)) { + // Give preference to error candidates that have no rest parameters (as they are more specific) + (candidatesForArgumentError || (candidatesForArgumentError = [])).push(checkCandidate); + continue; + } + } + candidates[candidateIndex] = checkCandidate; + return checkCandidate; } - candidates[candidateIndex] = checkCandidate; - return checkCandidate; - } - return undefined; + return undefined; + } } } @@ -35151,6 +35164,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } links.resolvedSignature = resolvingSignature; let result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal); + /** + * The following is an invariant condition beneath the above call to resolveSignature: + * - links.resolvedSignature is either the global constant readonly value `resolvingSignature` + * - or a calculated signature. + */ + Debug.assert(links.resolvedSignature); + // When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call // resolution should be deferred. if (result !== resolvingSignature) { @@ -35361,6 +35381,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkDeprecatedSignature(signature: Signature, node: CallLikeExpression) { + Debug.assert(signature); if (signature.flags & SignatureFlags.IsSignatureCandidateForOverloadFailure) return; if (signature.declaration && signature.declaration.flags & NodeFlags.Deprecated) { const suggestionNode = getDeprecatedSuggestionNode(node); @@ -38932,6 +38953,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const saveCurrentNode = currentNode; currentNode = node; instantiationCount = 0; + if (node.kind === SyntaxKind.CallExpression && chooseOverloadRecursionLevel >= 0) { + let setOfNode: Set | undefined; + if (chooseOverloadRecursionLevel >= 0 && (setOfNode = chooseOverloadFlushNodesSignaturesReq[chooseOverloadRecursionLevel])) { + if (!setOfNode.has(node)) { + /** + * The following is an invariant condition beneath the above call to resolveSignature: + * - getNodeLinks(node).resolvedSignature is either the global constant readonly value `resolvingSignature` + * - or a calculated signature. + * Therefore resetting must set to `resolvingSignature` - it means "not yet calculated". + */ + getNodeLinks(node).resolvedSignature = resolvingSignature; + setOfNode.add(node); + } + } + } const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple); const type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode); if (isConstEnumObjectType(type)) { diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload1.js b/tests/baselines/reference/arrayFilterBooleanExternalOverload1.js new file mode 100644 index 0000000000000..f37848ddd1c14 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload1.js @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts] //// + +//// [arrayFilterBooleanExternalOverload1.ts] +// #56013 + +// For reference, thise cases work as expected (no errors) when no external BooleanConstrudtor like overload is present +declare const maybe: boolean; +{ + const id = () => (t: T) => !!t; + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); + + result1; + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean + + result2; +} + + +//// [arrayFilterBooleanExternalOverload1.js] +"use strict"; +// #56013 +{ + const id = () => (t) => !!t; + const result1 = (maybe ? ['foo', 'bar', undefined] : [1]).filter(id()); + result1; + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean + result2; +} + + +//// [arrayFilterBooleanExternalOverload1.d.ts] +declare const maybe: boolean; diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload1.symbols b/tests/baselines/reference/arrayFilterBooleanExternalOverload1.symbols new file mode 100644 index 0000000000000..ac005eb5eab9f --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload1.symbols @@ -0,0 +1,38 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts] //// + +=== arrayFilterBooleanExternalOverload1.ts === +// #56013 + +// For reference, thise cases work as expected (no errors) when no external BooleanConstrudtor like overload is present +declare const maybe: boolean; +>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload1.ts, 3, 13)) +{ + const id = () => (t: T) => !!t; +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 9)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 16)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 25)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 16)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 25)) + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); +>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload1.ts, 7, 9)) +>(maybe ? ['foo', 'bar', undefined] : [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, --, --)) +>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload1.ts, 3, 13)) +>undefined : Symbol(undefined) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 9)) + + result1; +>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload1.ts, 7, 9)) + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean +>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload1.ts, 11, 9)) +>['foo', 'bar', undefined].filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>undefined : Symbol(undefined) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload1.ts, 5, 9)) + + result2; +>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload1.ts, 11, 9)) +} + diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload1.types b/tests/baselines/reference/arrayFilterBooleanExternalOverload1.types new file mode 100644 index 0000000000000..1b6cc5e140983 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload1.types @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts] //// + +=== arrayFilterBooleanExternalOverload1.ts === +// #56013 + +// For reference, thise cases work as expected (no errors) when no external BooleanConstrudtor like overload is present +declare const maybe: boolean; +>maybe : boolean +{ + const id = () => (t: T) => !!t; +>id : () => (t: T) => boolean +>() => (t: T) => !!t : () => (t: T) => boolean +>(t: T) => !!t : (t: T) => boolean +>t : T +>!!t : boolean +>!t : boolean +>t : T + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); +>result1 : (string | number | undefined)[] +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()) : (string | number | undefined)[] +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; } | { (predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; } +>(maybe ? ['foo', 'bar', undefined] : [1] ) : (string | undefined)[] | number[] +>maybe ? ['foo', 'bar', undefined] : [1] : (string | undefined)[] | number[] +>maybe : boolean +>['foo', 'bar', undefined] : (string | undefined)[] +>'foo' : "foo" +>'bar' : "bar" +>undefined : undefined +>[1] : number[] +>1 : 1 +>filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; } | { (predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; } +>id() : (t: string | number | undefined) => boolean +>id : () => (t: T) => boolean + + result1; +>result1 : (string | number | undefined)[] + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean +>result2 : (string | undefined)[] +>['foo', 'bar', undefined].filter(id()) : (string | undefined)[] +>['foo', 'bar', undefined].filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; } +>['foo', 'bar', undefined] : (string | undefined)[] +>'foo' : "foo" +>'bar' : "bar" +>undefined : undefined +>filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; } +>id() : (t: string | undefined) => boolean +>id : () => (t: T) => boolean + + result2; +>result2 : (string | undefined)[] +} + diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload2.errors.txt b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.errors.txt new file mode 100644 index 0000000000000..82136813ca67f --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.errors.txt @@ -0,0 +1,38 @@ +arrayFilterBooleanExternalOverload2.ts(13,71): error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. + Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. + Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + + +==== arrayFilterBooleanExternalOverload2.ts (1 errors) ==== + // #56013 + + const symbool = Symbol("MyBooleanSymbol"); + declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; + interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; + } + + declare const maybe: boolean; + { + const id = () => (t: T) => !!t; + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); // error before and after fix (so ignore type) + ~~~~ +!!! error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. +!!! error TS2345: Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. +!!! error TS2345: Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + // Errors before and after fix are different. + // The error in the #56013 fixed case is: + // ~~~~ + // error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. + // error TS2345: Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. + // error TS2345: Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + + + result1; + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean + + result2; + } + \ No newline at end of file diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload2.js b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.js new file mode 100644 index 0000000000000..3ef20a3606728 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.js @@ -0,0 +1,60 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload2.ts] //// + +//// [arrayFilterBooleanExternalOverload2.ts] +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +} + +declare const maybe: boolean; +{ + const id = () => (t: T) => !!t; + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); // error before and after fix (so ignore type) + // Errors before and after fix are different. + // The error in the #56013 fixed case is: + // ~~~~ + // error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. + // error TS2345: Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. + // error TS2345: Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + + + result1; + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean + + result2; +} + + +//// [arrayFilterBooleanExternalOverload2.js] +"use strict"; +// #56013 +const symbool = Symbol("MyBooleanSymbol"); +{ + const id = () => (t) => !!t; + const result1 = (maybe ? ['foo', 'bar', undefined] : [1]).filter(id()); // error before and after fix (so ignore type) + // Errors before and after fix are different. + // The error in the #56013 fixed case is: + // ~~~~ + // error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. + // error TS2345: Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. + // error TS2345: Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + result1; + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean + result2; +} + + +//// [arrayFilterBooleanExternalOverload2.d.ts] +declare const symbool: unique symbol; +declare const MyBoolean: typeof Boolean & { + prototype: typeof symbool; +}; +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +} +declare const maybe: boolean; diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload2.symbols b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.symbols new file mode 100644 index 0000000000000..062403d303e31 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.symbols @@ -0,0 +1,67 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload2.ts] //// + +=== arrayFilterBooleanExternalOverload2.ts === +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +>symbool : Symbol(symbool, Decl(arrayFilterBooleanExternalOverload2.ts, 2, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +>MyBoolean : Symbol(MyBoolean, Decl(arrayFilterBooleanExternalOverload2.ts, 3, 13)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>prototype : Symbol(prototype, Decl(arrayFilterBooleanExternalOverload2.ts, 3, 43)) +>symbool : Symbol(symbool, Decl(arrayFilterBooleanExternalOverload2.ts, 2, 5)) + +interface Array { +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 1 more) +>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 16)) + + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 20)) +>predicate : Symbol(predicate, Decl(arrayFilterBooleanExternalOverload2.ts, 5, 11)) +>MyBoolean : Symbol(MyBoolean, Decl(arrayFilterBooleanExternalOverload2.ts, 3, 13)) +>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 16)) +>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 16)) +} + +declare const maybe: boolean; +>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload2.ts, 8, 13)) +{ + const id = () => (t: T) => !!t; +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload2.ts, 10, 9)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload2.ts, 10, 16)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload2.ts, 10, 25)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload2.ts, 10, 16)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload2.ts, 10, 25)) + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); // error before and after fix (so ignore type) +>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload2.ts, 12, 9)) +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 20), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more) +>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload2.ts, 8, 13)) +>undefined : Symbol(undefined) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 20), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload2.ts, 10, 9)) + + // Errors before and after fix are different. + // The error in the #56013 fixed case is: + // ~~~~ + // error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. + // error TS2345: Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. + // error TS2345: Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + + + result1; +>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload2.ts, 12, 9)) + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean +>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload2.ts, 23, 9)) +>['foo', 'bar', undefined].filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 20)) +>undefined : Symbol(undefined) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload2.ts, 4, 20)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload2.ts, 10, 9)) + + result2; +>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload2.ts, 23, 9)) +} + diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload2.types b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.types new file mode 100644 index 0000000000000..45dd0f45a0f49 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload2.types @@ -0,0 +1,81 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload2.ts] //// + +=== arrayFilterBooleanExternalOverload2.ts === +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +>symbool : unique symbol +>Symbol("MyBooleanSymbol") : unique symbol +>Symbol : SymbolConstructor +>"MyBooleanSymbol" : "MyBooleanSymbol" + +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +>MyBoolean : BooleanConstructor & { prototype: typeof symbool; } +>Boolean : BooleanConstructor +>prototype : unique symbol +>symbool : unique symbol + +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +>filter : { (predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]; (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]; (predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; } +>predicate : BooleanConstructor & { prototype: unique symbol; } +>MyBoolean : BooleanConstructor & { prototype: unique symbol; } +>false : false +} + +declare const maybe: boolean; +>maybe : boolean +{ + const id = () => (t: T) => !!t; +>id : () => (t: T) => boolean +>() => (t: T) => !!t : () => (t: T) => boolean +>(t: T) => !!t : (t: T) => boolean +>t : T +>!!t : boolean +>!t : boolean +>t : T + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); // error before and after fix (so ignore type) +>result1 : number[] | string[] +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()) : number[] | string[] +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } | { (predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): number[]; } +>(maybe ? ['foo', 'bar', undefined] : [1] ) : (string | undefined)[] | number[] +>maybe ? ['foo', 'bar', undefined] : [1] : (string | undefined)[] | number[] +>maybe : boolean +>['foo', 'bar', undefined] : (string | undefined)[] +>'foo' : "foo" +>'bar' : "bar" +>undefined : undefined +>[1] : number[] +>1 : 1 +>filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } | { (predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): number[]; } +>id() : (t: unknown) => boolean +>id : () => (t: T) => boolean + + // Errors before and after fix are different. + // The error in the #56013 fixed case is: + // ~~~~ + // error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. + // error TS2345: Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. + // error TS2345: Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + + + result1; +>result1 : number[] | string[] + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean +>result2 : (string | undefined)[] +>['foo', 'bar', undefined].filter(id()) : (string | undefined)[] +>['foo', 'bar', undefined].filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } +>['foo', 'bar', undefined] : (string | undefined)[] +>'foo' : "foo" +>'bar' : "bar" +>undefined : undefined +>filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } +>id() : (t: string | undefined) => boolean +>id : () => (t: T) => boolean + + result2; +>result2 : (string | undefined)[] +} + diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload3.js b/tests/baselines/reference/arrayFilterBooleanExternalOverload3.js new file mode 100644 index 0000000000000..c7b637da5b723 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload3.js @@ -0,0 +1,47 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload3.ts] //// + +//// [arrayFilterBooleanExternalOverload3.ts] +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +} + +declare const maybe: boolean; +{ + const id = () => (t: T) => !!t; + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(MyBoolean); + + result1; + + const result2 = ['foo', 'bar', undefined].filter(MyBoolean); // want id() = (t: string) => boolean + + result2; +} + + +//// [arrayFilterBooleanExternalOverload3.js] +"use strict"; +// #56013 +const symbool = Symbol("MyBooleanSymbol"); +{ + const id = () => (t) => !!t; + const result1 = (maybe ? ['foo', 'bar', undefined] : [1]).filter(MyBoolean); + result1; + const result2 = ['foo', 'bar', undefined].filter(MyBoolean); // want id() = (t: string) => boolean + result2; +} + + +//// [arrayFilterBooleanExternalOverload3.d.ts] +declare const symbool: unique symbol; +declare const MyBoolean: typeof Boolean & { + prototype: typeof symbool; +}; +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +} +declare const maybe: boolean; diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload3.symbols b/tests/baselines/reference/arrayFilterBooleanExternalOverload3.symbols new file mode 100644 index 0000000000000..01dd0160f281d --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload3.symbols @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload3.ts] //// + +=== arrayFilterBooleanExternalOverload3.ts === +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +>symbool : Symbol(symbool, Decl(arrayFilterBooleanExternalOverload3.ts, 2, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +>MyBoolean : Symbol(MyBoolean, Decl(arrayFilterBooleanExternalOverload3.ts, 3, 13)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>prototype : Symbol(prototype, Decl(arrayFilterBooleanExternalOverload3.ts, 3, 43)) +>symbool : Symbol(symbool, Decl(arrayFilterBooleanExternalOverload3.ts, 2, 5)) + +interface Array { +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 1 more) +>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 16)) + + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 20)) +>predicate : Symbol(predicate, Decl(arrayFilterBooleanExternalOverload3.ts, 5, 11)) +>MyBoolean : Symbol(MyBoolean, Decl(arrayFilterBooleanExternalOverload3.ts, 3, 13)) +>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 16)) +>T : Symbol(T, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 16)) +} + +declare const maybe: boolean; +>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload3.ts, 8, 13)) +{ + const id = () => (t: T) => !!t; +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload3.ts, 10, 9)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload3.ts, 10, 16)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload3.ts, 10, 26)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload3.ts, 10, 16)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload3.ts, 10, 26)) + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(MyBoolean); +>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload3.ts, 12, 9)) +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 20), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more) +>maybe : Symbol(maybe, Decl(arrayFilterBooleanExternalOverload3.ts, 8, 13)) +>undefined : Symbol(undefined) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 20), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --) ... and 1 more) +>MyBoolean : Symbol(MyBoolean, Decl(arrayFilterBooleanExternalOverload3.ts, 3, 13)) + + result1; +>result1 : Symbol(result1, Decl(arrayFilterBooleanExternalOverload3.ts, 12, 9)) + + const result2 = ['foo', 'bar', undefined].filter(MyBoolean); // want id() = (t: string) => boolean +>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload3.ts, 16, 9)) +>['foo', 'bar', undefined].filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 20)) +>undefined : Symbol(undefined) +>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(arrayFilterBooleanExternalOverload3.ts, 4, 20)) +>MyBoolean : Symbol(MyBoolean, Decl(arrayFilterBooleanExternalOverload3.ts, 3, 13)) + + result2; +>result2 : Symbol(result2, Decl(arrayFilterBooleanExternalOverload3.ts, 16, 9)) +} + diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload3.types b/tests/baselines/reference/arrayFilterBooleanExternalOverload3.types new file mode 100644 index 0000000000000..2d184bd0e3de9 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload3.types @@ -0,0 +1,71 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload3.ts] //// + +=== arrayFilterBooleanExternalOverload3.ts === +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +>symbool : unique symbol +>Symbol("MyBooleanSymbol") : unique symbol +>Symbol : SymbolConstructor +>"MyBooleanSymbol" : "MyBooleanSymbol" + +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +>MyBoolean : BooleanConstructor & { prototype: typeof symbool; } +>Boolean : BooleanConstructor +>prototype : unique symbol +>symbool : unique symbol + +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +>filter : { (predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]; (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]; (predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; } +>predicate : BooleanConstructor & { prototype: unique symbol; } +>MyBoolean : BooleanConstructor & { prototype: unique symbol; } +>false : false +} + +declare const maybe: boolean; +>maybe : boolean +{ + const id = () => (t: T) => !!t; +>id : () => (t: T) => boolean +>() => (t: T) => !!t : () => (t: T) => boolean +>(t: T) => !!t : (t: T) => boolean +>t : T +>!!t : boolean +>!t : boolean +>t : T + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(MyBoolean); +>result1 : number[] | string[] +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter(MyBoolean) : number[] | string[] +>(maybe ? ['foo', 'bar', undefined] : [1] ).filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } | { (predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): number[]; } +>(maybe ? ['foo', 'bar', undefined] : [1] ) : (string | undefined)[] | number[] +>maybe ? ['foo', 'bar', undefined] : [1] : (string | undefined)[] | number[] +>maybe : boolean +>['foo', 'bar', undefined] : (string | undefined)[] +>'foo' : "foo" +>'bar' : "bar" +>undefined : undefined +>[1] : number[] +>1 : 1 +>filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } | { (predicate: (value: number, index: number, array: number[]) => value is S_1, thisArg?: any): S_1[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): number[]; } +>MyBoolean : BooleanConstructor & { prototype: unique symbol; } + + result1; +>result1 : number[] | string[] + + const result2 = ['foo', 'bar', undefined].filter(MyBoolean); // want id() = (t: string) => boolean +>result2 : string[] +>['foo', 'bar', undefined].filter(MyBoolean) : string[] +>['foo', 'bar', undefined].filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } +>['foo', 'bar', undefined] : (string | undefined)[] +>'foo' : "foo" +>'bar' : "bar" +>undefined : undefined +>filter : { (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | undefined, index: number, array: (string | undefined)[]) => unknown, thisArg?: any): (string | undefined)[]; (predicate: BooleanConstructor & { prototype: unique symbol; }): string[]; } +>MyBoolean : BooleanConstructor & { prototype: unique symbol; } + + result2; +>result2 : string[] +} + diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload4.js b/tests/baselines/reference/arrayFilterBooleanExternalOverload4.js new file mode 100644 index 0000000000000..23b4c372db118 --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload4.js @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload4.ts] //// + +//// [arrayFilterBooleanExternalOverload4.ts] +// #56013 + +interface F2 { + (p1:(t:1)=>T,p2:(u:1)=>U):11; + (p1:(t:1)=>T,p2:(u:U)=>U):19; + (p1:(t:T)=>T,p2:(u:1)=>U):91; + (p1:(t:T)=>T,p2:(u:U)=>U):99; +} +type ID = () => (i:I) => I; + +declare const id: ID; + + +const t11 = (0 as any as F2<1,1>)(id(),id()); +const t12 = (0 as any as F2<1,2>)(id(),id()); +const t21 = (0 as any as F2<2,1>)(id(),id()); +const t22 = (0 as any as F2<2,2>)(id(),id()); + +t11 satisfies 11; +t12 satisfies 19; +t21 satisfies 91; +t22 satisfies 99; + + + +//// [arrayFilterBooleanExternalOverload4.js] +"use strict"; +// #56013 +const t11 = 0(id(), id()); +const t12 = 0(id(), id()); +const t21 = 0(id(), id()); +const t22 = 0(id(), id()); +t11; +t12; +t21; +t22; + + +//// [arrayFilterBooleanExternalOverload4.d.ts] +interface F2 { + (p1: (t: 1) => T, p2: (u: 1) => U): 11; + (p1: (t: 1) => T, p2: (u: U) => U): 19; + (p1: (t: T) => T, p2: (u: 1) => U): 91; + (p1: (t: T) => T, p2: (u: U) => U): 99; +} +type ID = () => (i: I) => I; +declare const id: ID; +declare const t11: 11; +declare const t12: 19; +declare const t21: 91; +declare const t22: 99; diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload4.symbols b/tests/baselines/reference/arrayFilterBooleanExternalOverload4.symbols new file mode 100644 index 0000000000000..27acd4369f3ff --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload4.symbols @@ -0,0 +1,95 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload4.ts] //// + +=== arrayFilterBooleanExternalOverload4.ts === +// #56013 + +interface F2 { +>F2 : Symbol(F2, Decl(arrayFilterBooleanExternalOverload4.ts, 0, 0)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 13)) +>U : Symbol(U, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 30)) + + (p1:(t:1)=>T,p2:(u:1)=>U):11; +>p1 : Symbol(p1, Decl(arrayFilterBooleanExternalOverload4.ts, 3, 5)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload4.ts, 3, 9)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 13)) +>p2 : Symbol(p2, Decl(arrayFilterBooleanExternalOverload4.ts, 3, 17)) +>u : Symbol(u, Decl(arrayFilterBooleanExternalOverload4.ts, 3, 21)) +>U : Symbol(U, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 30)) + + (p1:(t:1)=>T,p2:(u:U)=>U):19; +>p1 : Symbol(p1, Decl(arrayFilterBooleanExternalOverload4.ts, 4, 5)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload4.ts, 4, 9)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 13)) +>p2 : Symbol(p2, Decl(arrayFilterBooleanExternalOverload4.ts, 4, 17)) +>u : Symbol(u, Decl(arrayFilterBooleanExternalOverload4.ts, 4, 21)) +>U : Symbol(U, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 30)) +>U : Symbol(U, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 30)) + + (p1:(t:T)=>T,p2:(u:1)=>U):91; +>p1 : Symbol(p1, Decl(arrayFilterBooleanExternalOverload4.ts, 5, 5)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload4.ts, 5, 9)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 13)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 13)) +>p2 : Symbol(p2, Decl(arrayFilterBooleanExternalOverload4.ts, 5, 17)) +>u : Symbol(u, Decl(arrayFilterBooleanExternalOverload4.ts, 5, 21)) +>U : Symbol(U, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 30)) + + (p1:(t:T)=>T,p2:(u:U)=>U):99; +>p1 : Symbol(p1, Decl(arrayFilterBooleanExternalOverload4.ts, 6, 5)) +>t : Symbol(t, Decl(arrayFilterBooleanExternalOverload4.ts, 6, 9)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 13)) +>T : Symbol(T, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 13)) +>p2 : Symbol(p2, Decl(arrayFilterBooleanExternalOverload4.ts, 6, 17)) +>u : Symbol(u, Decl(arrayFilterBooleanExternalOverload4.ts, 6, 21)) +>U : Symbol(U, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 30)) +>U : Symbol(U, Decl(arrayFilterBooleanExternalOverload4.ts, 2, 30)) +} +type ID = () => (i:I) => I; +>ID : Symbol(ID, Decl(arrayFilterBooleanExternalOverload4.ts, 7, 1)) +>I : Symbol(I, Decl(arrayFilterBooleanExternalOverload4.ts, 8, 11)) +>i : Symbol(i, Decl(arrayFilterBooleanExternalOverload4.ts, 8, 20)) +>I : Symbol(I, Decl(arrayFilterBooleanExternalOverload4.ts, 8, 11)) +>I : Symbol(I, Decl(arrayFilterBooleanExternalOverload4.ts, 8, 11)) + +declare const id: ID; +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) +>ID : Symbol(ID, Decl(arrayFilterBooleanExternalOverload4.ts, 7, 1)) + + +const t11 = (0 as any as F2<1,1>)(id(),id()); +>t11 : Symbol(t11, Decl(arrayFilterBooleanExternalOverload4.ts, 13, 5)) +>F2 : Symbol(F2, Decl(arrayFilterBooleanExternalOverload4.ts, 0, 0)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) + +const t12 = (0 as any as F2<1,2>)(id(),id()); +>t12 : Symbol(t12, Decl(arrayFilterBooleanExternalOverload4.ts, 14, 5)) +>F2 : Symbol(F2, Decl(arrayFilterBooleanExternalOverload4.ts, 0, 0)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) + +const t21 = (0 as any as F2<2,1>)(id(),id()); +>t21 : Symbol(t21, Decl(arrayFilterBooleanExternalOverload4.ts, 15, 5)) +>F2 : Symbol(F2, Decl(arrayFilterBooleanExternalOverload4.ts, 0, 0)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) + +const t22 = (0 as any as F2<2,2>)(id(),id()); +>t22 : Symbol(t22, Decl(arrayFilterBooleanExternalOverload4.ts, 16, 5)) +>F2 : Symbol(F2, Decl(arrayFilterBooleanExternalOverload4.ts, 0, 0)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) +>id : Symbol(id, Decl(arrayFilterBooleanExternalOverload4.ts, 10, 13)) + +t11 satisfies 11; +>t11 : Symbol(t11, Decl(arrayFilterBooleanExternalOverload4.ts, 13, 5)) + +t12 satisfies 19; +>t12 : Symbol(t12, Decl(arrayFilterBooleanExternalOverload4.ts, 14, 5)) + +t21 satisfies 91; +>t21 : Symbol(t21, Decl(arrayFilterBooleanExternalOverload4.ts, 15, 5)) + +t22 satisfies 99; +>t22 : Symbol(t22, Decl(arrayFilterBooleanExternalOverload4.ts, 16, 5)) + + diff --git a/tests/baselines/reference/arrayFilterBooleanExternalOverload4.types b/tests/baselines/reference/arrayFilterBooleanExternalOverload4.types new file mode 100644 index 0000000000000..aa23a50aaa99f --- /dev/null +++ b/tests/baselines/reference/arrayFilterBooleanExternalOverload4.types @@ -0,0 +1,103 @@ +//// [tests/cases/compiler/arrayFilterBooleanExternalOverload4.ts] //// + +=== arrayFilterBooleanExternalOverload4.ts === +// #56013 + +interface F2 { + (p1:(t:1)=>T,p2:(u:1)=>U):11; +>p1 : (t: 1) => T +>t : 1 +>p2 : (u: 1) => U +>u : 1 + + (p1:(t:1)=>T,p2:(u:U)=>U):19; +>p1 : (t: 1) => T +>t : 1 +>p2 : (u: U) => U +>u : U + + (p1:(t:T)=>T,p2:(u:1)=>U):91; +>p1 : (t: T) => T +>t : T +>p2 : (u: 1) => U +>u : 1 + + (p1:(t:T)=>T,p2:(u:U)=>U):99; +>p1 : (t: T) => T +>t : T +>p2 : (u: U) => U +>u : U +} +type ID = () => (i:I) => I; +>ID : () => (i: I) => I +>i : I + +declare const id: ID; +>id : ID + + +const t11 = (0 as any as F2<1,1>)(id(),id()); +>t11 : 11 +>(0 as any as F2<1,1>)(id(),id()) : 11 +>(0 as any as F2<1,1>) : F2<1, 1> +>0 as any as F2<1,1> : F2<1, 1> +>0 as any : any +>0 : 0 +>id() : (i: 1) => 1 +>id : ID +>id() : (i: 1) => 1 +>id : ID + +const t12 = (0 as any as F2<1,2>)(id(),id()); +>t12 : 19 +>(0 as any as F2<1,2>)(id(),id()) : 19 +>(0 as any as F2<1,2>) : F2<1, 2> +>0 as any as F2<1,2> : F2<1, 2> +>0 as any : any +>0 : 0 +>id() : (i: 1) => 1 +>id : ID +>id() : (i: 2) => 2 +>id : ID + +const t21 = (0 as any as F2<2,1>)(id(),id()); +>t21 : 91 +>(0 as any as F2<2,1>)(id(),id()) : 91 +>(0 as any as F2<2,1>) : F2<2, 1> +>0 as any as F2<2,1> : F2<2, 1> +>0 as any : any +>0 : 0 +>id() : (i: 2) => 2 +>id : ID +>id() : (i: 1) => 1 +>id : ID + +const t22 = (0 as any as F2<2,2>)(id(),id()); +>t22 : 99 +>(0 as any as F2<2,2>)(id(),id()) : 99 +>(0 as any as F2<2,2>) : F2<2, 2> +>0 as any as F2<2,2> : F2<2, 2> +>0 as any : any +>0 : 0 +>id() : (i: 2) => 2 +>id : ID +>id() : (i: 2) => 2 +>id : ID + +t11 satisfies 11; +>t11 satisfies 11 : 11 +>t11 : 11 + +t12 satisfies 19; +>t12 satisfies 19 : 19 +>t12 : 19 + +t21 satisfies 91; +>t21 satisfies 91 : 91 +>t21 : 91 + +t22 satisfies 99; +>t22 satisfies 99 : 99 +>t22 : 99 + + diff --git a/tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts b/tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts new file mode 100644 index 0000000000000..d85a94382182c --- /dev/null +++ b/tests/cases/compiler/arrayFilterBooleanExternalOverload1.ts @@ -0,0 +1,19 @@ +// @strict: true +// @target: es6 +// @declaration: true + +// #56013 + +// For reference, thise cases work as expected (no errors) when no external BooleanConstrudtor like overload is present +declare const maybe: boolean; +{ + const id = () => (t: T) => !!t; + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); + + result1; + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean + + result2; +} diff --git a/tests/cases/compiler/arrayFilterBooleanExternalOverload2.ts b/tests/cases/compiler/arrayFilterBooleanExternalOverload2.ts new file mode 100644 index 0000000000000..5a14517ae5295 --- /dev/null +++ b/tests/cases/compiler/arrayFilterBooleanExternalOverload2.ts @@ -0,0 +1,31 @@ +// @strict: true +// @target: es6 +// @declaration: true + +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +} + +declare const maybe: boolean; +{ + const id = () => (t: T) => !!t; + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(id()); // error before and after fix (so ignore type) + // Errors before and after fix are different. + // The error in the #56013 fixed case is: + // ~~~~ + // error TS2345: Argument of type '(t: unknown) => boolean' is not assignable to parameter of type 'BooleanConstructor & { prototype: unique symbol; }'. + // error TS2345: Type '(t: unknown) => boolean' is not assignable to type 'BooleanConstructor'. + // error TS2345: Type '(t: unknown) => boolean' provides no match for the signature 'new (value?: any): Boolean'. + + + result1; + + const result2 = ['foo', 'bar', undefined].filter(id()); // want id() = (t: string) => boolean + + result2; +} diff --git a/tests/cases/compiler/arrayFilterBooleanExternalOverload3.ts b/tests/cases/compiler/arrayFilterBooleanExternalOverload3.ts new file mode 100644 index 0000000000000..687fbeeac6a58 --- /dev/null +++ b/tests/cases/compiler/arrayFilterBooleanExternalOverload3.ts @@ -0,0 +1,24 @@ +// @strict: true +// @target: es6 +// @declaration: true + +// #56013 + +const symbool = Symbol("MyBooleanSymbol"); +declare const MyBoolean: typeof Boolean & { prototype: typeof symbool }; +interface Array { + filter(predicate: typeof MyBoolean): (T extends (0 | 0n | "" | false | null | undefined) ? never : T)[]; +} + +declare const maybe: boolean; +{ + const id = () => (t: T) => !!t; + + const result1 = (maybe ? ['foo', 'bar', undefined] : [1] ).filter(MyBoolean); + + result1; + + const result2 = ['foo', 'bar', undefined].filter(MyBoolean); // want id() = (t: string) => boolean + + result2; +} diff --git a/tests/cases/compiler/arrayFilterBooleanExternalOverload4.ts b/tests/cases/compiler/arrayFilterBooleanExternalOverload4.ts new file mode 100644 index 0000000000000..6efb146329d76 --- /dev/null +++ b/tests/cases/compiler/arrayFilterBooleanExternalOverload4.ts @@ -0,0 +1,27 @@ +// @strict: true +// @target: es6 +// @declaration: true + +// #56013 + +interface F2 { + (p1:(t:1)=>T,p2:(u:1)=>U):11; + (p1:(t:1)=>T,p2:(u:U)=>U):19; + (p1:(t:T)=>T,p2:(u:1)=>U):91; + (p1:(t:T)=>T,p2:(u:U)=>U):99; +} +type ID = () => (i:I) => I; + +declare const id: ID; + + +const t11 = (0 as any as F2<1,1>)(id(),id()); +const t12 = (0 as any as F2<1,2>)(id(),id()); +const t21 = (0 as any as F2<2,1>)(id(),id()); +const t22 = (0 as any as F2<2,2>)(id(),id()); + +t11 satisfies 11; +t12 satisfies 19; +t21 satisfies 91; +t22 satisfies 99; +