Skip to content

Widen inference candidates for error reporting #15221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
27 changes: 16 additions & 11 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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 ?
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
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) ====
function bar<T>(item1: T, item2: T) { }
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 '""'.
!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'.
8 changes: 4 additions & 4 deletions tests/baselines/reference/genericRestArgs.errors.txt
Original file line number Diff line number Diff line change
@@ -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[]'.


Expand All @@ -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<any>(1, "");
var a1Gc = makeArrayG<Object>(1, "");
var a1Gd = makeArrayG<number>(1, ""); // error
Expand All @@ -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<any>(1, "");
var a2Gc = makeArrayG<any[]>(1, ""); // error
~
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T>(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'.
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//// [noCommonSupertypeTypeInferenceMatchesReporting.ts]
// Fixes #15116, which asserted on line 5 but not 6
declare function f<T>(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
}
4 changes: 2 additions & 2 deletions tests/baselines/reference/parser15.4.4.14-9-2.errors.txt
Original file line number Diff line number Diff line change
@@ -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'.


Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @strictNullChecks: true
// Fixes #15116, which asserted on line 5 but not 6
declare function f<T>(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
}