Skip to content

Commit f6321bf

Browse files
authored
Elaborate into arrow return expressions and array types (#27040)
* Dive into simple arrow functions when elaborating errors * Dive into array literals as though they were tuples when elaborating, if possible * Make parameter required * Remove misleading errors by deeply tuplefying * Remove lib related spans
1 parent 577ee49 commit f6321bf

File tree

55 files changed

+517
-397
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+517
-397
lines changed

src/compiler/checker.ts

+70-17
Original file line numberDiff line numberDiff line change
@@ -10636,6 +10636,8 @@ namespace ts {
1063610636
return elaborateArrayLiteral(node as ArrayLiteralExpression, source, target, relation);
1063710637
case SyntaxKind.JsxAttributes:
1063810638
return elaborateJsxAttributes(node as JsxAttributes, source, target, relation);
10639+
case SyntaxKind.ArrowFunction:
10640+
return elaborateArrowFunction(node as ArrowFunction, source, target, relation);
1063910641
}
1064010642
return false;
1064110643
}
@@ -10661,6 +10663,46 @@ namespace ts {
1066110663
return false;
1066210664
}
1066310665

10666+
function elaborateArrowFunction(node: ArrowFunction, source: Type, target: Type, relation: Map<RelationComparisonResult>): boolean {
10667+
// Don't elaborate blocks
10668+
if (isBlock(node.body)) {
10669+
return false;
10670+
}
10671+
// Or functions with annotated parameter types
10672+
if (some(node.parameters, ts.hasType)) {
10673+
return false;
10674+
}
10675+
const sourceSig = getSingleCallSignature(source);
10676+
if (!sourceSig) {
10677+
return false;
10678+
}
10679+
const targetSignatures = getSignaturesOfType(target, SignatureKind.Call);
10680+
if (!length(targetSignatures)) {
10681+
return false;
10682+
}
10683+
const returnExpression = node.body;
10684+
const sourceReturn = getReturnTypeOfSignature(sourceSig);
10685+
const targetReturn = getUnionType(map(targetSignatures, getReturnTypeOfSignature));
10686+
if (!checkTypeRelatedTo(sourceReturn, targetReturn, relation, /*errorNode*/ undefined)) {
10687+
const elaborated = returnExpression && elaborateError(returnExpression, sourceReturn, targetReturn, relation, /*headMessage*/ undefined);
10688+
if (elaborated) {
10689+
return elaborated;
10690+
}
10691+
const resultObj: { error?: Diagnostic } = {};
10692+
checkTypeRelatedTo(sourceReturn, targetReturn, relation, returnExpression, /*message*/ undefined, /*chain*/ undefined, resultObj);
10693+
if (resultObj.error) {
10694+
if (target.symbol && length(target.symbol.declarations)) {
10695+
addRelatedInfo(resultObj.error, createDiagnosticForNode(
10696+
target.symbol.declarations[0],
10697+
Diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature,
10698+
));
10699+
}
10700+
return true;
10701+
}
10702+
}
10703+
return false;
10704+
}
10705+
1066410706
type ElaborationIterator = IterableIterator<{ errorNode: Node, innerExpression: Expression | undefined, nameType: Type, errorMessage?: DiagnosticMessage | undefined }>;
1066510707
/**
1066610708
* For every element returned from the iterator, checks that element to issue an error on a property of that element's type
@@ -10674,7 +10716,7 @@ namespace ts {
1067410716
const { errorNode: prop, innerExpression: next, nameType, errorMessage } = status.value;
1067510717
const sourcePropType = getIndexedAccessType(source, nameType, /*accessNode*/ undefined, errorType);
1067610718
const targetPropType = getIndexedAccessType(target, nameType, /*accessNode*/ undefined, errorType);
10677-
if (sourcePropType !== errorType && targetPropType !== errorType && !isTypeAssignableTo(sourcePropType, targetPropType)) {
10719+
if (sourcePropType !== errorType && targetPropType !== errorType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
1067810720
const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined);
1067910721
if (elaborated) {
1068010722
reportedError = true;
@@ -10699,19 +10741,22 @@ namespace ts {
1069910741
const indexInfo = isTypeAssignableToKind(nameType, TypeFlags.NumberLike) && getIndexInfoOfType(target, IndexKind.Number) ||
1070010742
getIndexInfoOfType(target, IndexKind.String) ||
1070110743
undefined;
10702-
if (indexInfo && indexInfo.declaration) {
10744+
if (indexInfo && indexInfo.declaration && !getSourceFileOfNode(indexInfo.declaration).hasNoDefaultLib) {
1070310745
issuedElaboration = true;
1070410746
addRelatedInfo(reportedDiag, createDiagnosticForNode(indexInfo.declaration, Diagnostics.The_expected_type_comes_from_this_index_signature));
1070510747
}
1070610748
}
1070710749

1070810750
if (!issuedElaboration && (targetProp && length(targetProp.declarations) || target.symbol && length(target.symbol.declarations))) {
10709-
addRelatedInfo(reportedDiag, createDiagnosticForNode(
10710-
targetProp && length(targetProp.declarations) ? targetProp.declarations[0] : target.symbol.declarations[0],
10711-
Diagnostics.The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1,
10712-
propertyName && !(nameType.flags & TypeFlags.UniqueESSymbol) ? unescapeLeadingUnderscores(propertyName) : typeToString(nameType),
10713-
typeToString(target)
10714-
));
10751+
const targetNode = targetProp && length(targetProp.declarations) ? targetProp.declarations[0] : target.symbol.declarations[0];
10752+
if (!getSourceFileOfNode(targetNode).hasNoDefaultLib) {
10753+
addRelatedInfo(reportedDiag, createDiagnosticForNode(
10754+
targetNode,
10755+
Diagnostics.The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1,
10756+
propertyName && !(nameType.flags & TypeFlags.UniqueESSymbol) ? unescapeLeadingUnderscores(propertyName) : typeToString(nameType),
10757+
typeToString(target)
10758+
));
10759+
}
1071510760
}
1071610761
}
1071710762
reportedError = true;
@@ -10750,6 +10795,11 @@ namespace ts {
1075010795
if (isTupleLikeType(source)) {
1075110796
return elaborateElementwise(generateLimitedTupleElements(node, target), source, target, relation);
1075210797
}
10798+
// recreate a tuple from the elements, if possible
10799+
const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true);
10800+
if (isTupleLikeType(tupleizedType)) {
10801+
return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation);
10802+
}
1075310803
return false;
1075410804
}
1075510805

@@ -16981,7 +17031,7 @@ namespace ts {
1698117031
(node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken);
1698217032
}
1698317033

16984-
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined): Type {
17034+
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined, forceTuple: boolean | undefined): Type {
1698517035
const elements = node.elements;
1698617036
const elementCount = elements.length;
1698717037
let hasNonEndingSpreadElement = false;
@@ -17003,7 +17053,7 @@ namespace ts {
1700317053
// get the contextual element type from it. So we do something similar to
1700417054
// getContextualTypeForElementExpression, which will crucially not error
1700517055
// if there is no index type / iterated type.
17006-
const restArrayType = checkExpression((<SpreadElement>e).expression, checkMode);
17056+
const restArrayType = checkExpression((<SpreadElement>e).expression, checkMode, forceTuple);
1700717057
const restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) ||
1700817058
getIteratedTypeOrElementType(restArrayType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false);
1700917059
if (restElementType) {
@@ -17012,7 +17062,7 @@ namespace ts {
1701217062
}
1701317063
else {
1701417064
const elementContextualType = getContextualTypeForElementExpression(contextualType, index);
17015-
const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType);
17065+
const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple);
1701617066
elementTypes.push(type);
1701717067
}
1701817068
if (index < elementCount - 1 && e.kind === SyntaxKind.SpreadElement) {
@@ -17050,6 +17100,9 @@ namespace ts {
1705017100
}
1705117101
return createTupleType(elementTypes, minLength, hasRestElement);
1705217102
}
17103+
else if (forceTuple) {
17104+
return createTupleType(elementTypes, minLength, hasRestElement);
17105+
}
1705317106
}
1705417107
return getArrayLiteralType(elementTypes, UnionReduction.Subtype);
1705517108
}
@@ -22097,11 +22150,11 @@ namespace ts {
2209722150
return false;
2209822151
}
2209922152

22100-
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type): Type {
22153+
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type {
2210122154
if (arguments.length === 2) {
2210222155
contextualType = getContextualType(node);
2210322156
}
22104-
const type = checkExpression(node, checkMode);
22157+
const type = checkExpression(node, checkMode, forceTuple);
2210522158
return isTypeAssertion(node) ? type :
2210622159
getWidenedLiteralLikeTypeForContextualType(type, contextualType);
2210722160
}
@@ -22201,13 +22254,13 @@ namespace ts {
2220122254
// object, it serves as an indicator that all contained function and arrow expressions should be considered to
2220222255
// have the wildcard function type; this form of type check is used during overload resolution to exclude
2220322256
// contextually typed function and arrow expressions in the initial phase.
22204-
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
22257+
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
2220522258
let type: Type;
2220622259
if (node.kind === SyntaxKind.QualifiedName) {
2220722260
type = checkQualifiedName(<QualifiedName>node);
2220822261
}
2220922262
else {
22210-
const uninstantiatedType = checkExpressionWorker(node, checkMode);
22263+
const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple);
2221122264
type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
2221222265
}
2221322266

@@ -22237,7 +22290,7 @@ namespace ts {
2223722290
return checkExpression(node.expression, checkMode);
2223822291
}
2223922292

22240-
function checkExpressionWorker(node: Expression, checkMode: CheckMode | undefined): Type {
22293+
function checkExpressionWorker(node: Expression, checkMode: CheckMode | undefined, forceTuple?: boolean): Type {
2224122294
switch (node.kind) {
2224222295
case SyntaxKind.Identifier:
2224322296
return checkIdentifier(<Identifier>node);
@@ -22262,7 +22315,7 @@ namespace ts {
2226222315
case SyntaxKind.RegularExpressionLiteral:
2226322316
return globalRegExpType;
2226422317
case SyntaxKind.ArrayLiteralExpression:
22265-
return checkArrayLiteral(<ArrayLiteralExpression>node, checkMode);
22318+
return checkArrayLiteral(<ArrayLiteralExpression>node, checkMode, forceTuple);
2226622319
case SyntaxKind.ObjectLiteralExpression:
2226722320
return checkObjectLiteral(<ObjectLiteralExpression>node, checkMode);
2226822321
case SyntaxKind.PropertyAccessExpression:

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3890,6 +3890,10 @@
38903890
"category": "Message",
38913891
"code": 6501
38923892
},
3893+
"The expected type comes from the return type of this signature.": {
3894+
"category": "Message",
3895+
"code": 6502
3896+
},
38933897

38943898
"Variable '{0}' implicitly has an '{1}' type.": {
38953899
"category": "Error",

tests/baselines/reference/arrayLiteralTypeInference.errors.txt

+19-17
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
tests/cases/compiler/arrayLiteralTypeInference.ts(14,14): error TS2322: Type '({ id: number; trueness: boolean; } | { id: number; name: string; })[]' is not assignable to type 'Action[]'.
2-
Type '{ id: number; trueness: boolean; } | { id: number; name: string; }' is not assignable to type 'Action'.
3-
Type '{ id: number; trueness: boolean; }' is not assignable to type 'Action'.
4-
Object literal may only specify known properties, and 'trueness' does not exist in type 'Action'.
5-
tests/cases/compiler/arrayLiteralTypeInference.ts(31,18): error TS2322: Type '({ id: number; trueness: boolean; } | { id: number; name: string; })[]' is not assignable to type '{ id: number; }[]'.
6-
Type '{ id: number; trueness: boolean; } | { id: number; name: string; }' is not assignable to type '{ id: number; }'.
7-
Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
8-
Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
1+
tests/cases/compiler/arrayLiteralTypeInference.ts(14,14): error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type 'Action'.
2+
Object literal may only specify known properties, and 'trueness' does not exist in type 'Action'.
3+
tests/cases/compiler/arrayLiteralTypeInference.ts(15,14): error TS2322: Type '{ id: number; name: string; }' is not assignable to type 'Action'.
4+
Object literal may only specify known properties, and 'name' does not exist in type 'Action'.
5+
tests/cases/compiler/arrayLiteralTypeInference.ts(31,18): error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
6+
Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
7+
tests/cases/compiler/arrayLiteralTypeInference.ts(32,18): error TS2322: Type '{ id: number; name: string; }' is not assignable to type '{ id: number; }'.
8+
Object literal may only specify known properties, and 'name' does not exist in type '{ id: number; }'.
99

1010

11-
==== tests/cases/compiler/arrayLiteralTypeInference.ts (2 errors) ====
11+
==== tests/cases/compiler/arrayLiteralTypeInference.ts (4 errors) ====
1212
class Action {
1313
id: number;
1414
}
@@ -24,11 +24,12 @@ tests/cases/compiler/arrayLiteralTypeInference.ts(31,18): error TS2322: Type '({
2424
var x1: Action[] = [
2525
{ id: 2, trueness: false },
2626
~~~~~~~~~~~~~~~
27-
!!! error TS2322: Type '({ id: number; trueness: boolean; } | { id: number; name: string; })[]' is not assignable to type 'Action[]'.
28-
!!! error TS2322: Type '{ id: number; trueness: boolean; } | { id: number; name: string; }' is not assignable to type 'Action'.
29-
!!! error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type 'Action'.
30-
!!! error TS2322: Object literal may only specify known properties, and 'trueness' does not exist in type 'Action'.
27+
!!! error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type 'Action'.
28+
!!! error TS2322: Object literal may only specify known properties, and 'trueness' does not exist in type 'Action'.
3129
{ id: 3, name: "three" }
30+
~~~~~~~~~~~~~
31+
!!! error TS2322: Type '{ id: number; name: string; }' is not assignable to type 'Action'.
32+
!!! error TS2322: Object literal may only specify known properties, and 'name' does not exist in type 'Action'.
3233
]
3334

3435
var x2: Action[] = [
@@ -46,11 +47,12 @@ tests/cases/compiler/arrayLiteralTypeInference.ts(31,18): error TS2322: Type '({
4647
[
4748
{ id: 2, trueness: false },
4849
~~~~~~~~~~~~~~~
49-
!!! error TS2322: Type '({ id: number; trueness: boolean; } | { id: number; name: string; })[]' is not assignable to type '{ id: number; }[]'.
50-
!!! error TS2322: Type '{ id: number; trueness: boolean; } | { id: number; name: string; }' is not assignable to type '{ id: number; }'.
51-
!!! error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
52-
!!! error TS2322: Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
50+
!!! error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
51+
!!! error TS2322: Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
5352
{ id: 3, name: "three" }
53+
~~~~~~~~~~~~~
54+
!!! error TS2322: Type '{ id: number; name: string; }' is not assignable to type '{ id: number; }'.
55+
!!! error TS2322: Object literal may only specify known properties, and 'name' does not exist in type '{ id: number; }'.
5456
]
5557

5658
var z2: { id: number }[] =

0 commit comments

Comments
 (0)