Skip to content

Commit bd1f8c5

Browse files
committed
Only check excess properties on final types from inference
1 parent 2d4938d commit bd1f8c5

File tree

1 file changed

+19
-12
lines changed

1 file changed

+19
-12
lines changed

src/compiler/checker.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15238,17 +15238,17 @@ namespace ts {
1523815238
if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) {
1523915239
// Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter)
1524015240
const paramType = getTypeAtPosition(signature, i);
15241-
let argType = getEffectiveArgumentType(node, i);
15242-
15243-
// If the effective argument type is 'undefined', there is no synthetic type
15244-
// for the argument. In that case, we should check the argument.
15245-
if (argType === undefined) {
15246-
argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
15247-
}
15248-
15241+
// If the effective argument type is undefined, there is no synthetic type for the argument.
15242+
// In that case, we should check the argument.
15243+
const argType = getEffectiveArgumentType(node, i) ||
15244+
checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined);
15245+
// If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter),
15246+
// we obtain the regular type of any object literal arguments because we may not have inferred complete
15247+
// parameter types yet and therefore excess property checks may yield false positives (see #17041).
15248+
const checkArgType = excludeArgument ? getRegularTypeOfObjectLiteral(argType) : argType;
1524915249
// Use argument expression as error location when reporting errors
1525015250
const errorNode = reportErrors ? getEffectiveArgumentErrorNode(node, i, arg) : undefined;
15251-
if (!checkTypeRelatedTo(argType, paramType, relation, errorNode, headMessage)) {
15251+
if (!checkTypeRelatedTo(checkArgType, paramType, relation, errorNode, headMessage)) {
1525215252
return false;
1525315253
}
1525415254
}
@@ -15620,6 +15620,7 @@ namespace ts {
1562015620
// For a decorator, no arguments are susceptible to contextual typing due to the fact
1562115621
// decorators are applied to a declaration by the emitter, and not to an expression.
1562215622
let excludeArgument: boolean[];
15623+
let excludeCount = 0;
1562315624
if (!isDecorator) {
1562415625
// We do not need to call `getEffectiveArgumentCount` here as it only
1562515626
// applies when calculating the number of arguments for a decorator.
@@ -15629,6 +15630,7 @@ namespace ts {
1562915630
excludeArgument = new Array(args.length);
1563015631
}
1563115632
excludeArgument[i] = true;
15633+
excludeCount++;
1563215634
}
1563315635
}
1563415636
}
@@ -15803,12 +15805,17 @@ namespace ts {
1580315805
candidateForArgumentError = candidate;
1580415806
break;
1580515807
}
15806-
const index = excludeArgument ? indexOf(excludeArgument, /*value*/ true) : -1;
15807-
if (index < 0) {
15808+
if (excludeCount === 0) {
1580815809
candidates[candidateIndex] = candidate;
1580915810
return candidate;
1581015811
}
15811-
excludeArgument[index] = false;
15812+
excludeCount--;
15813+
if (excludeCount > 0) {
15814+
excludeArgument[indexOf(excludeArgument, /*value*/ true)] = false;
15815+
}
15816+
else {
15817+
excludeArgument = undefined;
15818+
}
1581215819
}
1581315820
}
1581415821

0 commit comments

Comments
 (0)