diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5ff62eca473a9..3109aec227e84 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9920,7 +9920,7 @@ namespace ts { bestSupertypeScore = score; } - // types.length - 1 is the maximum score, given that getCommonSupertype returned false + Debug.assert(bestSupertypeScore < types.length, "types.length - 1 is the maximum score, given that getCommonSuperType returned false"); if (bestSupertypeScore === types.length - 1) { break; } @@ -10631,6 +10631,19 @@ namespace ts { return type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes); } + /** + * We widen inferred literal types if + * all inferences were made to top-level ocurrences of the type parameter, and + * the type parameter has no constraint or its constraint includes no primitive or literal types, and + * the type parameter was fixed during inference or does not occur at top-level in the return type. + */ + function widenInferenceCandidates(inference: InferenceInfo, signature: Signature) { + const widenLiteralTypes = inference.topLevel && + !hasPrimitiveConstraint(inference.typeParameter) && + (inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter)); + return widenLiteralTypes ? sameMap(inference.candidates, getWidenedLiteralType) : inference.candidates; + } + function hasPrimitiveConstraint(type: TypeParameter): boolean { const constraint = getConstraintOfTypeParameter(type); return constraint && maybeTypeOfKind(constraint, TypeFlags.Primitive | TypeFlags.Index); @@ -10642,15 +10655,7 @@ namespace ts { let inferenceSucceeded: boolean; if (!inferredType) { if (inference.candidates) { - // We widen inferred literal types if - // all inferences were made to top-level ocurrences of the type parameter, and - // the type parameter has no constraint or its constraint includes no primitive or literal types, and - // the type parameter was fixed during inference or does not occur at top-level in the return type. - const signature = context.signature; - const widenLiteralTypes = inference.topLevel && - !hasPrimitiveConstraint(inference.typeParameter) && - (inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter)); - const baseCandidates = widenLiteralTypes ? sameMap(inference.candidates, getWidenedLiteralType) : inference.candidates; + const baseCandidates = widenInferenceCandidates(inference, context.signature); // Infer widened union or supertype, or the unknown type for no common supertype. We infer union types // for inferences coming from return types in order to avoid common supertype failures. const unionOrSuperType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? @@ -15649,7 +15654,7 @@ namespace ts { else { Debug.assert(resultOfFailedInference.failedTypeParameterIndex >= 0); const failedTypeParameter = candidateForTypeArgumentError.typeParameters[resultOfFailedInference.failedTypeParameterIndex]; - const inferenceCandidates = resultOfFailedInference.inferences[resultOfFailedInference.failedTypeParameterIndex].candidates; + const inferenceCandidates = widenInferenceCandidates(resultOfFailedInference.inferences[resultOfFailedInference.failedTypeParameterIndex], resultOfFailedInference.signature); let diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, // details will be provided by call to reportNoCommonSupertypeError Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly, diff --git a/tests/baselines/reference/fixTypeParameterInSignatureWithRestParameters.errors.txt b/tests/baselines/reference/fixTypeParameterInSignatureWithRestParameters.errors.txt index e1cde0c163248..3666c89a0edd0 100644 --- a/tests/baselines/reference/fixTypeParameterInSignatureWithRestParameters.errors.txt +++ b/tests/baselines/reference/fixTypeParameterInSignatureWithRestParameters.errors.txt @@ -1,5 +1,5 @@ tests/cases/compiler/fixTypeParameterInSignatureWithRestParameters.ts(2,1): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. - Type argument candidate '1' is not a valid type argument because it is not a supertype of candidate '""'. + Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'. ==== tests/cases/compiler/fixTypeParameterInSignatureWithRestParameters.ts (1 errors) ==== @@ -7,4 +7,4 @@ tests/cases/compiler/fixTypeParameterInSignatureWithRestParameters.ts(2,1): erro bar(1, ""); // Should be ok ~~~ !!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. -!!! error TS2453: Type argument candidate '1' is not a valid type argument because it is not a supertype of candidate '""'. \ No newline at end of file +!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'. \ No newline at end of file diff --git a/tests/baselines/reference/genericRestArgs.errors.txt b/tests/baselines/reference/genericRestArgs.errors.txt index 2297277fe8d16..1dc8bd6f9779d 100644 --- a/tests/baselines/reference/genericRestArgs.errors.txt +++ b/tests/baselines/reference/genericRestArgs.errors.txt @@ -1,8 +1,8 @@ tests/cases/compiler/genericRestArgs.ts(2,12): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. - Type argument candidate '1' is not a valid type argument because it is not a supertype of candidate '""'. + Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'. tests/cases/compiler/genericRestArgs.ts(5,34): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. tests/cases/compiler/genericRestArgs.ts(10,12): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. - Type argument candidate '1' is not a valid type argument because it is not a supertype of candidate '""'. + Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'. tests/cases/compiler/genericRestArgs.ts(12,30): error TS2345: Argument of type '1' is not assignable to parameter of type 'any[]'. @@ -11,7 +11,7 @@ tests/cases/compiler/genericRestArgs.ts(12,30): error TS2345: Argument of type ' var a1Ga = makeArrayG(1, ""); // no error ~~~~~~~~~~ !!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. -!!! error TS2453: Type argument candidate '1' is not a valid type argument because it is not a supertype of candidate '""'. +!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'. var a1Gb = makeArrayG(1, ""); var a1Gc = makeArrayG(1, ""); var a1Gd = makeArrayG(1, ""); // error @@ -24,7 +24,7 @@ tests/cases/compiler/genericRestArgs.ts(12,30): error TS2345: Argument of type ' var a2Ga = makeArrayGOpt(1, ""); ~~~~~~~~~~~~~ !!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. -!!! error TS2453: Type argument candidate '1' is not a valid type argument because it is not a supertype of candidate '""'. +!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'. var a2Gb = makeArrayG(1, ""); var a2Gc = makeArrayG(1, ""); // error ~ diff --git a/tests/baselines/reference/noCommonSupertypeTypeInferenceMatchesReporting.errors.txt b/tests/baselines/reference/noCommonSupertypeTypeInferenceMatchesReporting.errors.txt new file mode 100644 index 0000000000000..58b3f502953f9 --- /dev/null +++ b/tests/baselines/reference/noCommonSupertypeTypeInferenceMatchesReporting.errors.txt @@ -0,0 +1,22 @@ +tests/cases/compiler/noCommonSupertypeTypeInferenceMatchesReporting.ts(4,5): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. + Type argument candidate '12 | undefined' is not a valid type argument because it is not a supertype of candidate 'number'. +tests/cases/compiler/noCommonSupertypeTypeInferenceMatchesReporting.ts(5,5): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. + Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate '12 | undefined'. + Type 'undefined' is not assignable to type 'number'. + + +==== tests/cases/compiler/noCommonSupertypeTypeInferenceMatchesReporting.ts (2 errors) ==== + // Fixes #15116, which asserted on line 5 but not 6 + declare function f(a: T, b: T): boolean; + function g(gut: { n: 12 | undefined }) { + f(gut.n, 12); // ok, T = number | undefined + ~ +!!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. +!!! error TS2453: Type argument candidate '12 | undefined' is not a valid type argument because it is not a supertype of candidate 'number'. + f(12, gut.n); // ok, T = number | undefined + ~ +!!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. +!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate '12 | undefined'. +!!! error TS2453: Type 'undefined' is not assignable to type 'number'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/noCommonSupertypeTypeInferenceMatchesReporting.js b/tests/baselines/reference/noCommonSupertypeTypeInferenceMatchesReporting.js new file mode 100644 index 0000000000000..b198635f81730 --- /dev/null +++ b/tests/baselines/reference/noCommonSupertypeTypeInferenceMatchesReporting.js @@ -0,0 +1,14 @@ +//// [noCommonSupertypeTypeInferenceMatchesReporting.ts] +// Fixes #15116, which asserted on line 5 but not 6 +declare function f(a: T, b: T): boolean; +function g(gut: { n: 12 | undefined }) { + f(gut.n, 12); // ok, T = number | undefined + f(12, gut.n); // ok, T = number | undefined +} + + +//// [noCommonSupertypeTypeInferenceMatchesReporting.js] +function g(gut) { + f(gut.n, 12); // ok, T = number | undefined + f(12, gut.n); // ok, T = number | undefined +} diff --git a/tests/baselines/reference/parser15.4.4.14-9-2.errors.txt b/tests/baselines/reference/parser15.4.4.14-9-2.errors.txt index 2c721724b3e76..48484e6917c3f 100644 --- a/tests/baselines/reference/parser15.4.4.14-9-2.errors.txt +++ b/tests/baselines/reference/parser15.4.4.14-9-2.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/parser/ecmascript5/parser15.4.4.14-9-2.ts(16,15): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. - Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'false'. + Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'boolean'. tests/cases/conformance/parser/ecmascript5/parser15.4.4.14-9-2.ts(25,1): error TS2304: Cannot find name 'runTestCase'. @@ -22,7 +22,7 @@ tests/cases/conformance/parser/ecmascript5/parser15.4.4.14-9-2.ts(25,1): error T var a = new Array(false,undefined,null,"0",obj,-1.3333333333333, "str",-0,true,+0, one, 1,0, false, _float, -(4/3)); ~~~~~ !!! error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly. -!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'false'. +!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'boolean'. if (a.indexOf(-(4/3)) === 14 && // a[14]=_float===-(4/3) a.indexOf(0) === 7 && // a[7] = +0, 0===+0 a.indexOf(-0) === 7 && // a[7] = +0, -0===+0 diff --git a/tests/cases/compiler/noCommonSupertypeTypeInferenceMatchesReporting.ts b/tests/cases/compiler/noCommonSupertypeTypeInferenceMatchesReporting.ts new file mode 100644 index 0000000000000..d0c6b1360d809 --- /dev/null +++ b/tests/cases/compiler/noCommonSupertypeTypeInferenceMatchesReporting.ts @@ -0,0 +1,7 @@ +// @strictNullChecks: true +// Fixes #15116, which asserted on line 5 but not 6 +declare function f(a: T, b: T): boolean; +function g(gut: { n: 12 | undefined }) { + f(gut.n, 12); // ok, T = number | undefined + f(12, gut.n); // ok, T = number | undefined +}