Skip to content

Commit 278d0a4

Browse files
committed
Do not use contextual signatures with too few parameters
1 parent 3e71d50 commit 278d0a4

File tree

4 files changed

+35
-27
lines changed

4 files changed

+35
-27
lines changed

src/compiler/checker.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10343,6 +10343,10 @@ namespace ts {
1034310343
if (!type) {
1034410344
return undefined;
1034510345
}
10346+
// If the contextual signature has fewer parameters than the function expression, do not use it
10347+
if (isFunctionExpressionOrArrowFunction(node) && isAritySmaller(type, node)) {
10348+
return undefined;
10349+
}
1034610350
if (!(type.flags & TypeFlags.Union)) {
1034710351
return getNonGenericSignature(type);
1034810352
}
@@ -10377,6 +10381,26 @@ namespace ts {
1037710381
return result;
1037810382
}
1037910383

10384+
function isAritySmaller(sourceType: Type, target: FunctionExpression | ArrowFunction) {
10385+
if (isFunctionType(sourceType)) {
10386+
let targetParameterCount = 0;
10387+
for (; targetParameterCount < target.parameters.length; targetParameterCount++) {
10388+
const param = target.parameters[targetParameterCount];
10389+
if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) {
10390+
break;
10391+
}
10392+
}
10393+
if (target.parameters.length && parameterIsThisKeyword(target.parameters[0])) {
10394+
targetParameterCount--;
10395+
}
10396+
const sourceSignatures = getSignaturesOfType(sourceType, SignatureKind.Call);
10397+
const sourceLengths = sourceSignatures.map(sig => !sig.hasRestParameter ? sig.parameters.length : Number.MAX_VALUE);
10398+
return forEach(sourceLengths, len => len < targetParameterCount);
10399+
}
10400+
10401+
return false;
10402+
}
10403+
1038010404
/**
1038110405
* Detect if the mapper implies an inference context. Specifically, there are 4 possible values
1038210406
* for a mapper. Let's go through each one of them:
@@ -11890,12 +11914,6 @@ namespace ts {
1189011914
// If the effective argument type is 'undefined', there is no synthetic type
1189111915
// for the argument. In that case, we should check the argument.
1189211916
if (argType === undefined) {
11893-
// If the parameter and argument are both functions and the parameter has fewer arguments than the argument,
11894-
// then this signature is not applicable. Exit early to avoid fixing incorrect
11895-
// contextual types to the function expression parameters.
11896-
if (!reportErrors && isAritySmaller(paramType, arg)) {
11897-
return false;
11898-
}
1189911917
argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
1190011918
}
1190111919

@@ -11910,16 +11928,6 @@ namespace ts {
1191011928
return true;
1191111929
}
1191211930

11913-
function isAritySmaller(sourceType: Type, target: Expression) {
11914-
if (isFunctionExpressionOrArrowFunction(target) && isFunctionType(sourceType)) {
11915-
const sourceSignatures = getSignaturesOfType(sourceType, SignatureKind.Call);
11916-
const sourceLengths = sourceSignatures.map(sig => !sig.hasRestParameter ? sig.parameters.length : Number.MAX_VALUE);
11917-
return forEach(sourceLengths, len => len < target.parameters.length);
11918-
}
11919-
11920-
return false;
11921-
}
11922-
1192311931
/**
1192411932
* Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise.
1192511933
*/

tests/baselines/reference/contextualTypingOfTooShortOverloads.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ var use: Overload;
77
use((req, res) => {});
88
>use((req, res) => {}) : void
99
>use : Overload
10-
>(req, res) => {} : (req: number, res: number) => void
11-
>req : number
12-
>res : number
10+
>(req, res) => {} : (req: any, res: any) => void
11+
>req : any
12+
>res : any
1313

1414
interface Overload {
1515
>Overload : Overload

tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ module GenericParameter {
122122
>'' : ""
123123

124124
var r11 = foo6(<T>(x: T, y?: T) => ''); // any => string (+1 overload)
125-
>r11 : any
126-
>foo6(<T>(x: T, y?: T) => '') : any
125+
>r11 : { (x: any): string; (x: any, y?: any): string; }
126+
>foo6(<T>(x: T, y?: T) => '') : { (x: any): string; (x: any, y?: any): string; }
127127
>foo6 : <T>(cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
128128
><T>(x: T, y?: T) => '' : <T>(x: T, y?: T) => string
129129
>T : T

tests/baselines/reference/partiallyAnnotatedFunctionInferenceError.errors.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
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'.
2-
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'.
3-
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'.
1+
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'.
2+
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'.
3+
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'.
44

55

66
==== tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partiallyAnnotatedFunctionInferenceError.ts (3 errors) ====
@@ -17,11 +17,11 @@ tests/cases/conformance/types/contextualTypes/partiallyAnnotatedFunction/partial
1717
// more args
1818
testError((t1: D, t2, t3) => {})
1919
~~~~~~~~~~~~~~~~~~~~~
20-
!!! error TS2345: Argument of type '(t1: D, t2: C, t3: any) => void' is not assignable to parameter of type '(t: C, t1: C) => void'.
20+
!!! error TS2345: Argument of type '(t1: D, t2: any, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'.
2121
testError((t1, t2: D, t3) => {})
2222
~~~~~~~~~~~~~~~~~~~~~
23-
!!! error TS2345: Argument of type '(t1: C, t2: D, t3: any) => void' is not assignable to parameter of type '(t: C, t1: C) => void'.
23+
!!! error TS2345: Argument of type '(t1: any, t2: D, t3: any) => void' is not assignable to parameter of type '(t: any, t1: any) => void'.
2424
testError((t1, t2, t3: D) => {})
2525
~~~~~~~~~~~~~~~~~~~~~
26-
!!! error TS2345: Argument of type '(t1: C, t2: C, t3: D) => void' is not assignable to parameter of type '(t: C, t1: C) => void'.
26+
!!! error TS2345: Argument of type '(t1: any, t2: any, t3: D) => void' is not assignable to parameter of type '(t: any, t1: any) => void'.
2727

0 commit comments

Comments
 (0)