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
This commit is contained in:
Wesley Wigham
2018-09-17 16:45:54 -07:00
committed by GitHub
parent 577ee49106
commit f6321bf6d5
55 changed files with 517 additions and 397 deletions

View File

@@ -10636,6 +10636,8 @@ namespace ts {
return elaborateArrayLiteral(node as ArrayLiteralExpression, source, target, relation);
case SyntaxKind.JsxAttributes:
return elaborateJsxAttributes(node as JsxAttributes, source, target, relation);
case SyntaxKind.ArrowFunction:
return elaborateArrowFunction(node as ArrowFunction, source, target, relation);
}
return false;
}
@@ -10661,6 +10663,46 @@ namespace ts {
return false;
}
function elaborateArrowFunction(node: ArrowFunction, source: Type, target: Type, relation: Map<RelationComparisonResult>): boolean {
// Don't elaborate blocks
if (isBlock(node.body)) {
return false;
}
// Or functions with annotated parameter types
if (some(node.parameters, ts.hasType)) {
return false;
}
const sourceSig = getSingleCallSignature(source);
if (!sourceSig) {
return false;
}
const targetSignatures = getSignaturesOfType(target, SignatureKind.Call);
if (!length(targetSignatures)) {
return false;
}
const returnExpression = node.body;
const sourceReturn = getReturnTypeOfSignature(sourceSig);
const targetReturn = getUnionType(map(targetSignatures, getReturnTypeOfSignature));
if (!checkTypeRelatedTo(sourceReturn, targetReturn, relation, /*errorNode*/ undefined)) {
const elaborated = returnExpression && elaborateError(returnExpression, sourceReturn, targetReturn, relation, /*headMessage*/ undefined);
if (elaborated) {
return elaborated;
}
const resultObj: { error?: Diagnostic } = {};
checkTypeRelatedTo(sourceReturn, targetReturn, relation, returnExpression, /*message*/ undefined, /*chain*/ undefined, resultObj);
if (resultObj.error) {
if (target.symbol && length(target.symbol.declarations)) {
addRelatedInfo(resultObj.error, createDiagnosticForNode(
target.symbol.declarations[0],
Diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature,
));
}
return true;
}
}
return false;
}
type ElaborationIterator = IterableIterator<{ errorNode: Node, innerExpression: Expression | undefined, nameType: Type, errorMessage?: DiagnosticMessage | undefined }>;
/**
* 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 {
const { errorNode: prop, innerExpression: next, nameType, errorMessage } = status.value;
const sourcePropType = getIndexedAccessType(source, nameType, /*accessNode*/ undefined, errorType);
const targetPropType = getIndexedAccessType(target, nameType, /*accessNode*/ undefined, errorType);
if (sourcePropType !== errorType && targetPropType !== errorType && !isTypeAssignableTo(sourcePropType, targetPropType)) {
if (sourcePropType !== errorType && targetPropType !== errorType && !checkTypeRelatedTo(sourcePropType, targetPropType, relation, /*errorNode*/ undefined)) {
const elaborated = next && elaborateError(next, sourcePropType, targetPropType, relation, /*headMessage*/ undefined);
if (elaborated) {
reportedError = true;
@@ -10699,19 +10741,22 @@ namespace ts {
const indexInfo = isTypeAssignableToKind(nameType, TypeFlags.NumberLike) && getIndexInfoOfType(target, IndexKind.Number) ||
getIndexInfoOfType(target, IndexKind.String) ||
undefined;
if (indexInfo && indexInfo.declaration) {
if (indexInfo && indexInfo.declaration && !getSourceFileOfNode(indexInfo.declaration).hasNoDefaultLib) {
issuedElaboration = true;
addRelatedInfo(reportedDiag, createDiagnosticForNode(indexInfo.declaration, Diagnostics.The_expected_type_comes_from_this_index_signature));
}
}
if (!issuedElaboration && (targetProp && length(targetProp.declarations) || target.symbol && length(target.symbol.declarations))) {
addRelatedInfo(reportedDiag, createDiagnosticForNode(
targetProp && length(targetProp.declarations) ? targetProp.declarations[0] : target.symbol.declarations[0],
Diagnostics.The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1,
propertyName && !(nameType.flags & TypeFlags.UniqueESSymbol) ? unescapeLeadingUnderscores(propertyName) : typeToString(nameType),
typeToString(target)
));
const targetNode = targetProp && length(targetProp.declarations) ? targetProp.declarations[0] : target.symbol.declarations[0];
if (!getSourceFileOfNode(targetNode).hasNoDefaultLib) {
addRelatedInfo(reportedDiag, createDiagnosticForNode(
targetNode,
Diagnostics.The_expected_type_comes_from_property_0_which_is_declared_here_on_type_1,
propertyName && !(nameType.flags & TypeFlags.UniqueESSymbol) ? unescapeLeadingUnderscores(propertyName) : typeToString(nameType),
typeToString(target)
));
}
}
}
reportedError = true;
@@ -10750,6 +10795,11 @@ namespace ts {
if (isTupleLikeType(source)) {
return elaborateElementwise(generateLimitedTupleElements(node, target), source, target, relation);
}
// recreate a tuple from the elements, if possible
const tupleizedType = checkArrayLiteral(node, CheckMode.Contextual, /*forceTuple*/ true);
if (isTupleLikeType(tupleizedType)) {
return elaborateElementwise(generateLimitedTupleElements(node, target), tupleizedType, target, relation);
}
return false;
}
@@ -16981,7 +17031,7 @@ namespace ts {
(node.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>node).operatorToken.kind === SyntaxKind.EqualsToken);
}
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined): Type {
function checkArrayLiteral(node: ArrayLiteralExpression, checkMode: CheckMode | undefined, forceTuple: boolean | undefined): Type {
const elements = node.elements;
const elementCount = elements.length;
let hasNonEndingSpreadElement = false;
@@ -17003,7 +17053,7 @@ namespace ts {
// get the contextual element type from it. So we do something similar to
// getContextualTypeForElementExpression, which will crucially not error
// if there is no index type / iterated type.
const restArrayType = checkExpression((<SpreadElement>e).expression, checkMode);
const restArrayType = checkExpression((<SpreadElement>e).expression, checkMode, forceTuple);
const restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) ||
getIteratedTypeOrElementType(restArrayType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*allowAsyncIterables*/ false, /*checkAssignability*/ false);
if (restElementType) {
@@ -17012,7 +17062,7 @@ namespace ts {
}
else {
const elementContextualType = getContextualTypeForElementExpression(contextualType, index);
const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType);
const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple);
elementTypes.push(type);
}
if (index < elementCount - 1 && e.kind === SyntaxKind.SpreadElement) {
@@ -17050,6 +17100,9 @@ namespace ts {
}
return createTupleType(elementTypes, minLength, hasRestElement);
}
else if (forceTuple) {
return createTupleType(elementTypes, minLength, hasRestElement);
}
}
return getArrayLiteralType(elementTypes, UnionReduction.Subtype);
}
@@ -22097,11 +22150,11 @@ namespace ts {
return false;
}
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type): Type {
function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, contextualType?: Type, forceTuple?: boolean): Type {
if (arguments.length === 2) {
contextualType = getContextualType(node);
}
const type = checkExpression(node, checkMode);
const type = checkExpression(node, checkMode, forceTuple);
return isTypeAssertion(node) ? type :
getWidenedLiteralLikeTypeForContextualType(type, contextualType);
}
@@ -22201,13 +22254,13 @@ namespace ts {
// object, it serves as an indicator that all contained function and arrow expressions should be considered to
// have the wildcard function type; this form of type check is used during overload resolution to exclude
// contextually typed function and arrow expressions in the initial phase.
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode): Type {
function checkExpression(node: Expression | QualifiedName, checkMode?: CheckMode, forceTuple?: boolean): Type {
let type: Type;
if (node.kind === SyntaxKind.QualifiedName) {
type = checkQualifiedName(<QualifiedName>node);
}
else {
const uninstantiatedType = checkExpressionWorker(node, checkMode);
const uninstantiatedType = checkExpressionWorker(node, checkMode, forceTuple);
type = instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode);
}
@@ -22237,7 +22290,7 @@ namespace ts {
return checkExpression(node.expression, checkMode);
}
function checkExpressionWorker(node: Expression, checkMode: CheckMode | undefined): Type {
function checkExpressionWorker(node: Expression, checkMode: CheckMode | undefined, forceTuple?: boolean): Type {
switch (node.kind) {
case SyntaxKind.Identifier:
return checkIdentifier(<Identifier>node);
@@ -22262,7 +22315,7 @@ namespace ts {
case SyntaxKind.RegularExpressionLiteral:
return globalRegExpType;
case SyntaxKind.ArrayLiteralExpression:
return checkArrayLiteral(<ArrayLiteralExpression>node, checkMode);
return checkArrayLiteral(<ArrayLiteralExpression>node, checkMode, forceTuple);
case SyntaxKind.ObjectLiteralExpression:
return checkObjectLiteral(<ObjectLiteralExpression>node, checkMode);
case SyntaxKind.PropertyAccessExpression:

View File

@@ -3890,6 +3890,10 @@
"category": "Message",
"code": 6501
},
"The expected type comes from the return type of this signature.": {
"category": "Message",
"code": 6502
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",