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.
This commit is contained in:
parent
f307948af0
commit
83fdecf86a
1 changed files with 20 additions and 0 deletions
|
@ -11890,6 +11890,11 @@ namespace ts {
|
||||||
// If the effective argument type is 'undefined', there is no synthetic type
|
// If the effective argument type is 'undefined', there is no synthetic type
|
||||||
// for the argument. In that case, we should check the argument.
|
// for the argument. In that case, we should check the argument.
|
||||||
if (argType === undefined) {
|
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);
|
argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11904,6 +11909,21 @@ namespace ts {
|
||||||
return true;
|
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.
|
* Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue