From bd1f8c50a4b7c05736345e42c4592e02c3ae78c9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 26 Jul 2017 07:16:06 -0700 Subject: [PATCH] Only check excess properties on final types from inference --- src/compiler/checker.ts | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ff45ee0b7..870c5255fc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15238,17 +15238,17 @@ namespace ts { if (arg === undefined || arg.kind !== SyntaxKind.OmittedExpression) { // Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter) const paramType = getTypeAtPosition(signature, i); - let argType = getEffectiveArgumentType(node, i); - - // 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) { - argType = checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); - } - + // If the effective argument type is undefined, there is no synthetic type for the argument. + // In that case, we should check the argument. + const argType = getEffectiveArgumentType(node, i) || + checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + // If one or more arguments are still excluded (as indicated by a non-null excludeArgument parameter), + // we obtain the regular type of any object literal arguments because we may not have inferred complete + // parameter types yet and therefore excess property checks may yield false positives (see #17041). + const checkArgType = excludeArgument ? getRegularTypeOfObjectLiteral(argType) : argType; // Use argument expression as error location when reporting errors const errorNode = reportErrors ? getEffectiveArgumentErrorNode(node, i, arg) : undefined; - if (!checkTypeRelatedTo(argType, paramType, relation, errorNode, headMessage)) { + if (!checkTypeRelatedTo(checkArgType, paramType, relation, errorNode, headMessage)) { return false; } } @@ -15620,6 +15620,7 @@ namespace ts { // For a decorator, no arguments are susceptible to contextual typing due to the fact // decorators are applied to a declaration by the emitter, and not to an expression. let excludeArgument: boolean[]; + let excludeCount = 0; if (!isDecorator) { // We do not need to call `getEffectiveArgumentCount` here as it only // applies when calculating the number of arguments for a decorator. @@ -15629,6 +15630,7 @@ namespace ts { excludeArgument = new Array(args.length); } excludeArgument[i] = true; + excludeCount++; } } } @@ -15803,12 +15805,17 @@ namespace ts { candidateForArgumentError = candidate; break; } - const index = excludeArgument ? indexOf(excludeArgument, /*value*/ true) : -1; - if (index < 0) { + if (excludeCount === 0) { candidates[candidateIndex] = candidate; return candidate; } - excludeArgument[index] = false; + excludeCount--; + if (excludeCount > 0) { + excludeArgument[indexOf(excludeArgument, /*value*/ true)] = false; + } + else { + excludeArgument = undefined; + } } }