diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 88376a3dcb0..8748fd6db95 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10337,6 +10337,10 @@ namespace ts { if (!type) { return undefined; } + // If the contextual signature has fewer parameters than the function expression, do not use it + if (isFunctionExpressionOrArrowFunction(node) && isAritySmaller(type, node)) { + return undefined; + } if (!(type.flags & TypeFlags.Union)) { return getNonGenericSignature(type); } @@ -10371,6 +10375,26 @@ namespace ts { return result; } + function isAritySmaller(sourceType: Type, target: FunctionExpression | ArrowFunction) { + if (isFunctionType(sourceType)) { + let targetParameterCount = 0; + for (; targetParameterCount < target.parameters.length; targetParameterCount++) { + const param = target.parameters[targetParameterCount]; + if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) { + break; + } + } + if (target.parameters.length && parameterIsThisKeyword(target.parameters[0])) { + targetParameterCount--; + } + const sourceSignatures = getSignaturesOfType(sourceType, SignatureKind.Call); + const sourceLengths = sourceSignatures.map(sig => !sig.hasRestParameter ? sig.parameters.length : Number.MAX_VALUE); + return forEach(sourceLengths, len => len < targetParameterCount); + } + + return false; + } + /** * Detect if the mapper implies an inference context. Specifically, there are 4 possible values * for a mapper. Let's go through each one of them: @@ -11884,12 +11908,6 @@ namespace ts { // If the effective argument type is 'undefined', there is no synthetic type // for the argument. In that case, we should check the argument. if (argType === undefined) { - // If the parameter and argument are both functions and the parameter has fewer arguments than the argument, - // then this signature is not applicable. Exit early to avoid fixing incorrect - // contextual types to the function expression parameters. - if (!reportErrors && isAritySmaller(paramType, arg)) { - return false; - } argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); } @@ -11904,16 +11922,6 @@ namespace ts { return true; } - function isAritySmaller(sourceType: Type, target: Expression) { - if (isFunctionExpressionOrArrowFunction(target) && isFunctionType(sourceType)) { - const sourceSignatures = getSignaturesOfType(sourceType, SignatureKind.Call); - const sourceLengths = sourceSignatures.map(sig => !sig.hasRestParameter ? sig.parameters.length : Number.MAX_VALUE); - return forEach(sourceLengths, len => len < target.parameters.length); - } - - return false; - } - /** * Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise. */ diff --git a/tests/baselines/reference/contextualTypingOfTooShortOverloads.types b/tests/baselines/reference/contextualTypingOfTooShortOverloads.types index 9e306da3e61..769bd55d4b5 100644 --- a/tests/baselines/reference/contextualTypingOfTooShortOverloads.types +++ b/tests/baselines/reference/contextualTypingOfTooShortOverloads.types @@ -7,9 +7,9 @@ var use: Overload; use((req, res) => {}); >use((req, res) => {}) : void >use : Overload ->(req, res) => {} : (req: number, res: number) => void ->req : number ->res : number +>(req, res) => {} : (req: any, res: any) => void +>req : any +>res : any interface Overload { >Overload : Overload diff --git a/tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments.types b/tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments.types index c26370a7640..ab07e9c880a 100644 --- a/tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments.types +++ b/tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments.types @@ -122,8 +122,8 @@ module GenericParameter { >'' : "" var r11 = foo6((x: T, y?: T) => ''); // any => string (+1 overload) ->r11 : any ->foo6((x: T, y?: T) => '') : any +>r11 : { (x: any): string; (x: any, y?: any): string; } +>foo6((x: T, y?: T) => '') : { (x: any): string; (x: any, y?: any): string; } >foo6 : (cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; } >(x: T, y?: T) => '' : (x: T, y?: T) => string >T : T diff --git a/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt b/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt index d1e84494fa9..a6a45245929 100644 --- a/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt +++ b/tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt @@ -1,6 +1,6 @@ -tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(12,11): error TS2345: Argument of type '(t1: D, t2: C, t3: any) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. -tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(13,11): error TS2345: Argument of type '(t1: C, t2: D, t3: any) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. -tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(14,11): error TS2345: Argument of type '(t1: C, t2: C, t3: D) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. +tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(12,11): error TS2345: Argument of type '(t1: D, t2: any, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. +tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(13,11): error TS2345: Argument of type '(t1: any, t2: D, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. +tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts(14,11): error TS2345: Argument of type '(t1: any, t2: any, t3: D) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. ==== tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts (3 errors) ==== @@ -17,11 +17,11 @@ tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partial // more args testError((t1: D, t2, t3) => {}) ~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(t1: D, t2: C, t3: any) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. +!!! error TS2345: Argument of type '(t1: D, t2: any, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. testError((t1, t2: D, t3) => {}) ~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(t1: C, t2: D, t3: any) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. +!!! error TS2345: Argument of type '(t1: any, t2: D, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. testError((t1, t2, t3: D) => {}) ~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2345: Argument of type '(t1: C, t2: C, t3: D) => void' is not assignable to parameter of type '(t: C, t1: C) => void'. +!!! error TS2345: Argument of type '(t1: any, t2: any, t3: D) => void' is not assignable to parameter of type '(t: any, t1: any) => void'. \ No newline at end of file