Skip to content

Elaborate into arrow return expressions and array types #27040

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

Merged
merged 7 commits into from
Sep 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 70 additions & 17 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10624,6 +10624,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;
}
Expand All @@ -10649,6 +10651,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
Expand All @@ -10662,7 +10704,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;
Expand All @@ -10687,19 +10729,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;
Expand Down Expand Up @@ -10738,6 +10783,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;
}

Expand Down Expand Up @@ -16969,7 +17019,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;
Expand All @@ -16991,7 +17041,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) {
Expand All @@ -17000,7 +17050,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) {
Expand Down Expand Up @@ -17038,6 +17088,9 @@ namespace ts {
}
return createTupleType(elementTypes, minLength, hasRestElement);
}
else if (forceTuple) {
return createTupleType(elementTypes, minLength, hasRestElement);
}
}
return getArrayLiteralType(elementTypes, UnionReduction.Subtype);
}
Expand Down Expand Up @@ -22085,11 +22138,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);
}
Expand Down Expand Up @@ -22189,13 +22242,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);
}

Expand Down Expand Up @@ -22225,7 +22278,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);
Expand All @@ -22250,7 +22303,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:
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3886,6 +3886,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",
Expand Down
36 changes: 19 additions & 17 deletions tests/baselines/reference/arrayLiteralTypeInference.errors.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
tests/cases/compiler/arrayLiteralTypeInference.ts(14,14): error TS2322: Type '({ id: number; trueness: boolean; } | { id: number; name: string; })[]' is not assignable to type 'Action[]'.
Type '{ id: number; trueness: boolean; } | { id: number; name: string; }' is not assignable to type 'Action'.
Type '{ id: number; trueness: boolean; }' is not assignable to type 'Action'.
Object literal may only specify known properties, and 'trueness' does not exist in type 'Action'.
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; }[]'.
Type '{ id: number; trueness: boolean; } | { id: number; name: string; }' is not assignable to type '{ id: number; }'.
Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
tests/cases/compiler/arrayLiteralTypeInference.ts(14,14): error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type 'Action'.
Object literal may only specify known properties, and 'trueness' does not exist in type 'Action'.
tests/cases/compiler/arrayLiteralTypeInference.ts(15,14): error TS2322: Type '{ id: number; name: string; }' is not assignable to type 'Action'.
Object literal may only specify known properties, and 'name' does not exist in type 'Action'.
tests/cases/compiler/arrayLiteralTypeInference.ts(31,18): error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
tests/cases/compiler/arrayLiteralTypeInference.ts(32,18): error TS2322: Type '{ id: number; name: string; }' is not assignable to type '{ id: number; }'.
Object literal may only specify known properties, and 'name' does not exist in type '{ id: number; }'.


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

var x2: Action[] = [
Expand All @@ -46,11 +47,12 @@ tests/cases/compiler/arrayLiteralTypeInference.ts(31,18): error TS2322: Type '({
[
{ id: 2, trueness: false },
~~~~~~~~~~~~~~~
!!! error TS2322: Type '({ id: number; trueness: boolean; } | { id: number; name: string; })[]' is not assignable to type '{ id: number; }[]'.
!!! error TS2322: Type '{ id: number; trueness: boolean; } | { id: number; name: string; }' is not assignable to type '{ id: number; }'.
!!! error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
!!! error TS2322: Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
!!! error TS2322: Type '{ id: number; trueness: boolean; }' is not assignable to type '{ id: number; }'.
!!! error TS2322: Object literal may only specify known properties, and 'trueness' does not exist in type '{ id: number; }'.
{ id: 3, name: "three" }
~~~~~~~~~~~~~
!!! error TS2322: Type '{ id: number; name: string; }' is not assignable to type '{ id: number; }'.
!!! error TS2322: Object literal may only specify known properties, and 'name' does not exist in type '{ id: number; }'.
]

var z2: { id: number }[] =
Expand Down
Loading