From 83fdecf86a77f5ed5470e91029b0730dcdfaed83 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 27 Oct 2016 14:41:40 -0700 Subject: [PATCH] Skip overloads with too-short function parameters If the parameter of an overload is a function and the argument is also a function, skip the overload if the parameter has fewer arguments than the argument does. That overload cannot possibly apply, and should not participate in, for example, contextual typing. Example: ```ts interface I { (a: number): void; (b: string, c): void; } declare function f(i: I): void; f((x, y) => {}); ``` This code now skips the first overload instead of considering. This was a longstanding bug but was only uncovered now that more functions expressions are context sensitive. --- src/compiler/checker.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4aee170f195..7cf61a71317 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11890,6 +11890,11 @@ 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. + if (!reportErrors && isAritySmaller(paramType, arg)) { + return false; + } argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); } @@ -11904,6 +11909,21 @@ namespace ts { return true; } + function isAritySmaller(sourceType: Type, target: Expression) { + if (isFunctionExpressionOrArrowFunction(target) && 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; + } + } + 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); + } + } + /** * Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise. */