Skip to content

Commit 9865e09

Browse files
committed
Report type argument inference errors on specific candidates
1 parent 05300a7 commit 9865e09

31 files changed

+318
-116
lines changed

src/compiler/checker.ts

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3138,33 +3138,43 @@ module ts {
31383138
var identityRelation: Map<boolean> = {};
31393139

31403140
function isTypeIdenticalTo(source: Type, target: Type): boolean {
3141-
return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
3141+
return checkTypeRelatedTo(source, target, identityRelation,
3142+
/*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined, /*containingMessageChain*/ undefined);
31423143
}
31433144

31443145
function isTypeSubtypeOf(source: Type, target: Type): boolean {
3145-
return checkTypeSubtypeOf(source, target, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
3146+
return checkTypeSubtypeOf(source, target, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined, /*containingMessageChain*/ undefined);
31463147
}
31473148

3148-
function checkTypeSubtypeOf(source: Type, target: Type, errorNode: Node, chainedMessage: DiagnosticMessage, terminalMessage: DiagnosticMessage): boolean {
3149-
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, chainedMessage, terminalMessage);
3149+
function checkTypeSubtypeOf(
3150+
source: Type,
3151+
target: Type,
3152+
errorNode: Node,
3153+
chainedMessage: DiagnosticMessage,
3154+
terminalMessage: DiagnosticMessage,
3155+
containingMessageChain: DiagnosticMessageChain): boolean {
3156+
3157+
return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, chainedMessage, terminalMessage, containingMessageChain);
31503158
}
31513159

31523160
function isTypeAssignableTo(source: Type, target: Type): boolean {
31533161
return checkTypeAssignableTo(source, target, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
31543162
}
31553163

31563164
function checkTypeAssignableTo(source: Type, target: Type, errorNode: Node, chainedMessage: DiagnosticMessage, terminalMessage: DiagnosticMessage): boolean {
3157-
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, chainedMessage, terminalMessage);
3165+
return checkTypeRelatedTo(source, target, assignableRelation, errorNode, chainedMessage, terminalMessage, /*containingMessageChain*/ undefined);
31583166
}
31593167

31603168
function isTypeRelatedTo(source: Type, target: Type, relation: Map<boolean>): boolean {
3161-
return checkTypeRelatedTo(source, target, relation, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
3169+
return checkTypeRelatedTo(source, target, relation,
3170+
/*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined, /*containingMessageChain*/ undefined);
31623171
}
31633172

31643173
function isSignatureAssignableTo(source: Signature, target: Signature): boolean {
31653174
var sourceType = getOrCreateTypeFromSignature(source);
31663175
var targetType = getOrCreateTypeFromSignature(target);
3167-
return checkTypeRelatedTo(sourceType, targetType, assignableRelation, /*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
3176+
return checkTypeRelatedTo(sourceType, targetType, assignableRelation,
3177+
/*errorNode*/ undefined, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined, /*containingMessageChain*/ undefined);
31683178
}
31693179

31703180
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
@@ -3228,7 +3238,15 @@ module ts {
32283238
}
32293239
}
32303240

3231-
function checkTypeRelatedTo(source: Type, target: Type, relation: Map<boolean>, errorNode: Node, chainedMessage: DiagnosticMessage, terminalMessage: DiagnosticMessage): boolean {
3241+
function checkTypeRelatedTo(
3242+
source: Type,
3243+
target: Type,
3244+
relation: Map<boolean>,
3245+
errorNode: Node,
3246+
chainedMessage: DiagnosticMessage,
3247+
terminalMessage: DiagnosticMessage,
3248+
containingMessageChain: DiagnosticMessageChain): boolean {
3249+
32323250
var errorInfo: DiagnosticMessageChain;
32333251
var sourceStack: ObjectType[];
32343252
var targetStack: ObjectType[];
@@ -3243,6 +3261,9 @@ module ts {
32433261
error(errorNode, Diagnostics.Excessive_stack_depth_comparing_types_0_and_1, typeToString(source), typeToString(target));
32443262
}
32453263
else if (errorInfo) {
3264+
if (containingMessageChain) {
3265+
errorInfo = concatenateDiagnosticMessageChains(containingMessageChain, errorInfo);
3266+
}
32463267
addDiagnostic(createDiagnosticForNodeFromMessageChain(errorNode, errorInfo, program.getCompilerHost().getNewLine()));
32473268
}
32483269
return result;
@@ -3738,6 +3759,43 @@ module ts {
37383759
return forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined);
37393760
}
37403761

3762+
function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void {
3763+
var bestSupertype: Type;
3764+
var bestSupertypeDownfallType: Type; // The type that caused bestSupertype not to be the common supertype
3765+
var bestSupertypeScore = 0;
3766+
3767+
for (var i = 0; i < types.length; i++) {
3768+
var score = 0;
3769+
var downfallType: Type = undefined;
3770+
for (var j = 0; j < types.length; j++) {
3771+
if (types[i] === types[j] || isTypeSubtypeOf(types[j], types[i])) {
3772+
score++;
3773+
}
3774+
else if (!downfallType) {
3775+
downfallType = types[j];
3776+
}
3777+
}
3778+
3779+
if (score > bestSupertypeScore) {
3780+
bestSupertype = types[i];
3781+
bestSupertypeDownfallType = downfallType;
3782+
bestSupertypeScore = score;
3783+
}
3784+
3785+
// types.length - 1 is the maximum score, given that getCommonSupertype returned false
3786+
if (bestSupertypeScore === types.length - 1) {
3787+
break;
3788+
}
3789+
}
3790+
3791+
// In the following errors, the {1} slot is before the {0} slot because checkTypeSubtypeOf supplies the
3792+
// subtype as the first argument to the error
3793+
checkTypeSubtypeOf(bestSupertypeDownfallType, bestSupertype, errorLocation,
3794+
Diagnostics.Type_argument_candidate_1_is_not_a_valid_type_argument_because_it_is_not_a_supertype_of_candidate_0_Colon,
3795+
Diagnostics.Type_argument_candidate_1_is_not_a_valid_type_argument_because_it_is_not_a_supertype_of_candidate_0,
3796+
errorMessageChainHead);
3797+
}
3798+
37413799
function isTypeOfObjectLiteral(type: Type): boolean {
37423800
return (type.flags & TypeFlags.Anonymous) && type.symbol && (type.symbol.flags & SymbolFlags.ObjectLiteral) ? true : false;
37433801
}
@@ -4020,7 +4078,7 @@ module ts {
40204078
for (var i = 0; i < context.inferredTypes.length; i++) {
40214079
getInferredType(context, i);
40224080
}
4023-
context.inferences = undefined;
4081+
40244082
return context.inferredTypes;
40254083
}
40264084

@@ -5145,7 +5203,8 @@ module ts {
51455203
// Use argument expression as error location when reporting errors
51465204
var isValidArgument = checkTypeRelatedTo(argType, paramType, relation, reportErrors ? arg : undefined,
51475205
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1,
5148-
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1);
5206+
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1,
5207+
/*containingMessageChain*/ undefined);
51495208
if (!isValidArgument) {
51505209
return false;
51515210
}
@@ -5203,8 +5262,14 @@ module ts {
52035262
}
52045263
else {
52055264
Debug.assert(resultOfFailedInference.failureIndex >= 0);
5206-
error(node.func, Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Try_specifying_the_type_arguments_explicitly,
5207-
typeToString(candidateForTypeArgumentError.typeParameters[resultOfFailedInference.failureIndex]));
5265+
var failedTypeParameter = candidateForTypeArgumentError.typeParameters[resultOfFailedInference.failureIndex];
5266+
var inferenceCandidates = resultOfFailedInference.inferences[resultOfFailedInference.failureIndex];
5267+
5268+
var diagnosticChainHead = chainDiagnosticMessages(/*details*/ undefined, // details will be provided by call to reportNoCommonSupertypeError
5269+
Diagnostics.The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly_Colon,
5270+
typeToString(failedTypeParameter));
5271+
5272+
reportNoCommonSupertypeError(inferenceCandidates, node.func, diagnosticChainHead);
52085273
}
52095274
}
52105275
else {

src/compiler/core.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@ module ts {
272272
};
273273
}
274274

275+
export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): DiagnosticMessageChain {
276+
Debug.assert(!headChain.next);
277+
headChain.next = tailChain;
278+
return headChain;
279+
}
280+
275281
export function flattenDiagnosticChain(file: SourceFile, start: number, length: number, diagnosticChain: DiagnosticMessageChain, newLine: string): Diagnostic {
276282
Debug.assert(start >= 0, "start must be non-negative, is " + start);
277283
Debug.assert(length >= 0, "length must be non-negative, is " + length);

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,9 @@ module ts {
261261
Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses: { code: 2445, category: DiagnosticCategory.Error, key: "Property '{0}' is protected and only accessible within class '{1}' and its subclasses." },
262262
Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1: { code: 2446, category: DiagnosticCategory.Error, key: "Property '{0}' is protected and only accessible through an instance of class '{1}'." },
263263
The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead: { code: 2447, category: DiagnosticCategory.Error, key: "The '{0}' operator is not allowed for boolean types. Consider using '{1}' instead." },
264-
The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Try_specifying_the_type_arguments_explicitly: { code: 2448, category: DiagnosticCategory.Error, key: "The type argument for type parameter '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly." },
264+
The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly_Colon: { code: 2448, category: DiagnosticCategory.Error, key: "The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly:" },
265+
Type_argument_candidate_1_is_not_a_valid_type_argument_because_it_is_not_a_supertype_of_candidate_0_Colon: { code: 2449, category: DiagnosticCategory.Error, key: "Type argument candidate '{1}' is not a valid type argument because it is not a supertype of candidate '{0}':" },
266+
Type_argument_candidate_1_is_not_a_valid_type_argument_because_it_is_not_a_supertype_of_candidate_0: { code: 2450, category: DiagnosticCategory.Error, key: "Type argument candidate '{1}' is not a valid type argument because it is not a supertype of candidate '{0}'." },
265267
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
266268
Type_parameter_0_of_exported_class_has_or_is_using_name_1_from_private_module_2: { code: 4001, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using name '{1}' from private module '{2}'." },
267269
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },

src/compiler/diagnosticMessages.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,11 +1036,20 @@
10361036
"category": "Error",
10371037
"code": 2447
10381038
},
1039-
"The type argument for type parameter '{0}' cannot be inferred from the usage. Try specifying the type arguments explicitly.": {
1039+
"The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly:": {
10401040
"category": "Error",
10411041
"code": 2448
10421042
},
10431043

1044+
"Type argument candidate '{1}' is not a valid type argument because it is not a supertype of candidate '{0}':": {
1045+
"category": "Error",
1046+
"code": 2449
1047+
},
1048+
"Type argument candidate '{1}' is not a valid type argument because it is not a supertype of candidate '{0}'.": {
1049+
"category": "Error",
1050+
"code": 2450
1051+
},
1052+
10441053
"Import declaration '{0}' is using private name '{1}'.": {
10451054
"category": "Error",
10461055
"code": 4000
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(2,22): error TS2339: Property 'foo' does not exist on type 'string'.
2-
tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(3,10): error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Try specifying the type arguments explicitly.
2+
tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(3,10): error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly:
3+
Type argument candidate 'string' is not a valid type argument because it is not a supertype of candidate 'number'.
34

45

56
==== tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts (2 errors) ====
@@ -9,4 +10,5 @@ tests/cases/compiler/contextualTypingWithFixedTypeParameters1.ts(3,10): error TS
910
!!! error TS2339: Property 'foo' does not exist on type 'string'.
1011
var r9 = f10('', () => (a => a.foo), 1); // error
1112
~~~
12-
!!! error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Try specifying the type arguments explicitly.
13+
!!! error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly:
14+
!!! error TS2448: Type argument candidate 'string' is not a valid type argument because it is not a supertype of candidate 'number'.

tests/baselines/reference/defaultBestCommonTypesHaveDecls.errors.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
tests/cases/compiler/defaultBestCommonTypesHaveDecls.ts(2,6): error TS2339: Property 'length' does not exist on type '{}'.
22
tests/cases/compiler/defaultBestCommonTypesHaveDecls.ts(5,6): error TS2339: Property 'length' does not exist on type 'Object'.
3-
tests/cases/compiler/defaultBestCommonTypesHaveDecls.ts(8,14): error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Try specifying the type arguments explicitly.
3+
tests/cases/compiler/defaultBestCommonTypesHaveDecls.ts(8,14): error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly:
4+
Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'.
45

56

67
==== tests/cases/compiler/defaultBestCommonTypesHaveDecls.ts (3 errors) ====
@@ -17,7 +18,8 @@ tests/cases/compiler/defaultBestCommonTypesHaveDecls.ts(8,14): error TS2448: The
1718
function concat<T>(x: T, y: T): T { return null; }
1819
var result = concat(1, ""); // error
1920
~~~~~~
20-
!!! error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Try specifying the type arguments explicitly.
21+
!!! error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly:
22+
!!! error TS2448: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'.
2123
var elementCount = result.length;
2224

2325
function concat2<T, U>(x: T, y: U) { return null; }
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
tests/cases/compiler/fixTypeParameterInSignatureWithRestParameters.ts(2,1): error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Try specifying the type arguments explicitly.
1+
tests/cases/compiler/fixTypeParameterInSignatureWithRestParameters.ts(2,1): error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly:
2+
Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'.
23

34

45
==== tests/cases/compiler/fixTypeParameterInSignatureWithRestParameters.ts (1 errors) ====
56
function bar<T>(item1: T, item2: T) { }
67
bar(1, ""); // Should be ok
78
~~~
8-
!!! error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Try specifying the type arguments explicitly.
9+
!!! error TS2448: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly:
10+
!!! error TS2448: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate 'string'.

0 commit comments

Comments
 (0)