From ad823daabba75c8d6d77ab5f9194ce5feaa1fa96 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 5 Mar 2019 06:09:54 -0800 Subject: [PATCH] Consistently defer generic functions to second type inference pass --- src/compiler/checker.ts | 27 +++++++++++++++++++-------- src/compiler/types.ts | 1 + 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4493b9e00a3..8cceb85a19a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20138,7 +20138,7 @@ namespace ts { const paramType = getTypeAtPosition(signature, i); // For context sensitive arguments we pass the identityMapper, which is a signal to treat all // context sensitive function expressions as wildcards - const checkMode = (excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | + const checkMode = (excludeArgument && i < excludeArgument.length && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); inferTypes(context.inferences, argType, paramType); @@ -20241,7 +20241,7 @@ namespace ts { // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, // can be specified by users through attributes property. const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node); - const checkMode = excludeArgument && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; + const checkMode = excludeArgument && excludeArgument.length > 0 && excludeArgument[0] ? CheckMode.SkipContextSensitive : 0; const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined, checkMode); return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes); } @@ -20276,13 +20276,13 @@ namespace ts { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { const paramType = getTypeAtPosition(signature, i); - const checkMode = (excludeArgument && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | + const checkMode = (excludeArgument && i < excludeArgument.length && excludeArgument[i] ? CheckMode.SkipContextSensitive : 0) | (excludeArgument ? CheckMode.SkipGenericFunctions : 0); const argType = checkExpressionWithContextualType(arg, paramType, /*contextualMapper*/ undefined, checkMode); // 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; + const checkArgType = excludeArgument && excludeArgument.length ? getRegularTypeOfObjectLiteral(argType) : argType; if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage)) { return false; } @@ -20665,7 +20665,7 @@ namespace ts { } else { inferenceContext = createInferenceContext(candidate.typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext); + typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument || emptyArray, inferenceContext); } checkCandidate = getSignatureInstantiation(candidate, typeArgumentTypes, isInJSFile(candidate.declaration), inferenceContext && inferenceContext.inferredTypeParameters); // If the original signature has a generic rest type, instantiation may produce a @@ -20678,14 +20678,14 @@ namespace ts { else { checkCandidate = candidate; } - if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument, /*reportErrors*/ false)) { + if (!checkApplicableSignature(node, args, checkCandidate, relation, excludeArgument || inferenceContext && emptyArray, /*reportErrors*/ false)) { // Give preference to error candidates that have no rest parameters (as they are more specific) if (!candidateForArgumentError || getEffectiveRestType(candidateForArgumentError) || !getEffectiveRestType(checkCandidate)) { candidateForArgumentError = checkCandidate; } continue; } - if (excludeArgument) { + if (excludeArgument || inferenceContext && inferenceContext.flags & InferenceFlags.SkippedGenericFunction) { // If one or more context sensitive arguments were excluded, we start including // them now (and keeping do so for any subsequent candidates) and perform a second // round of type inference and applicability checking for this particular candidate. @@ -20830,7 +20830,7 @@ namespace ts { function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: ReadonlyArray, candidate: Signature, args: ReadonlyArray): Signature { const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJSFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None); - const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args), inferenceContext); + const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args) || emptyArray, inferenceContext); return createSignatureInstantiation(candidate, typeArgumentTypes); } @@ -20932,6 +20932,7 @@ namespace ts { // sensitive arguments are being deferred) and every call signature is generic and returns a function type, // we return resolvingSignature here. This result will be propagated out and turned into anyFunctionType. if (checkMode & CheckMode.SkipGenericFunctions && callSignatures.every(isGenericFunctionReturningFunction)) { + skippedGenericFunction(node, checkMode); return resolvingSignature; } // If the function is explicitly marked with `@class`, then it must be constructed. @@ -23387,6 +23388,7 @@ namespace ts { const signature = getSingleCallSignature(type); if (signature && signature.typeParameters) { if (checkMode & CheckMode.SkipGenericFunctions) { + skippedGenericFunction(node, checkMode); return anyFunctionType; } const contextualType = getApparentTypeOfContextualType(node); @@ -23429,6 +23431,15 @@ namespace ts { return type; } + function skippedGenericFunction(node: Node, checkMode: CheckMode) { + if (checkMode & CheckMode.Inferential) { + // We have skipped a generic function during inferential typing. Obtain the inference context and + // indicate this has occurred such that we know a second pass of inference is be needed. + const context = getContextualMapper(node); + context.flags |= InferenceFlags.SkippedGenericFunction; + } + } + function hasInferenceCandidates(info: InferenceInfo) { return !!(info.candidates || info.contraCandidates); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5217dd66fb4..0d24db8454e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4362,6 +4362,7 @@ namespace ts { NoDefault = 1 << 0, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType) AnyDefault = 1 << 1, // Infer anyType for no inferences (otherwise emptyObjectType) NoFixing = 1 << 2, // Disable type parameter fixing + SkippedGenericFunction = 1 << 3, } /**