Improve unification by moving more logic to getIndexedAccessType

This commit is contained in:
Anders Hejlsberg
2016-11-01 10:17:08 -07:00
parent 6b28d21a82
commit 41c20545e1

View File

@@ -5738,14 +5738,24 @@ namespace ts {
return indexedAccessTypes[objectType.id] || (indexedAccessTypes[objectType.id] = createIndexedAccessType(objectType, indexType));
}
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode?: ElementAccessExpression | IndexedAccessTypeNode) {
function getPropertyTypeForIndexType(objectType: Type, indexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode, cacheSymbol: boolean) {
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? <ElementAccessExpression>accessNode : undefined;
if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
const prop = getPropertyOfType(objectType, escapeIdentifier((<LiteralType>indexType).text));
const propName = indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral) ?
(<LiteralType>indexType).text :
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
getPropertyNameForKnownSymbolName((<Identifier>(<PropertyAccessExpression>accessExpression.argumentExpression).name).text) :
undefined;
if (propName) {
const prop = getPropertyOfType(objectType, propName);
if (prop) {
if (accessExpression && isAssignmentTarget(accessExpression) && isReadonlySymbol(prop)) {
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, symbolToString(prop));
return unknownType;
if (accessExpression) {
if (isAssignmentTarget(accessExpression) && (isReferenceToReadonlyEntity(accessExpression, prop) || isReferenceThroughNamespaceImport(accessExpression))) {
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, symbolToString(prop));
return unknownType;
}
if (cacheSymbol) {
getNodeLinks(accessNode).resolvedSymbol = prop;
}
}
return getTypeOfSymbol(prop);
}
@@ -5798,10 +5808,11 @@ namespace ts {
}
return getIndexedAccessTypeForTypeParameter(objectType, <TypeParameter>indexType);
}
const apparentType = getApparentType(objectType);
if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) {
const propTypes: Type[] = [];
for (const t of (<UnionType>indexType).types) {
const propType = getPropertyTypeForIndexType(objectType, t, accessNode);
const propType = getPropertyTypeForIndexType(apparentType, t, accessNode, /*cacheSymbol*/ false);
if (propType === unknownType) {
return unknownType;
}
@@ -5809,7 +5820,7 @@ namespace ts {
}
return getUnionType(propTypes);
}
return getPropertyTypeForIndexType(objectType, indexType, accessNode);
return getPropertyTypeForIndexType(apparentType, indexType, accessNode, /*cacheSymbol*/ true);
}
function getTypeFromIndexedAccessTypeNode(node: IndexedAccessTypeNode) {
@@ -11644,55 +11655,9 @@ namespace ts {
return unknownType;
}
const propName = getPropertyNameForIndexedAccess(indexExpression, indexType);
if (propName !== undefined) {
const prop = getPropertyOfType(getApparentType(objectType), propName);
if (prop) {
getNodeLinks(node).resolvedSymbol = prop;
if (isAssignmentTarget(node)) {
if (isReferenceToReadonlyEntity(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>node)) {
error(indexExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, propName);
return unknownType;
}
}
return getTypeOfSymbol(prop);
}
}
return getIndexedAccessType(objectType, indexType, node);
}
/**
* If indexArgumentExpression is a string literal or number literal, returns its text.
* If indexArgumentExpression is a constant value, returns its string value.
* If indexArgumentExpression is a well known symbol, returns the property name corresponding
* to this symbol, as long as it is a proper symbol reference.
* Otherwise, returns undefined.
*/
function getPropertyNameForIndexedAccess(indexExpression: Expression, indexType: Type): string {
if (indexExpression.kind === SyntaxKind.StringLiteral || indexExpression.kind === SyntaxKind.NumericLiteral) {
return (<LiteralExpression>indexExpression).text;
}
if (indexExpression.kind === SyntaxKind.ElementAccessExpression || indexExpression.kind === SyntaxKind.PropertyAccessExpression) {
const value = getConstantValue(<ElementAccessExpression | PropertyAccessExpression>indexExpression);
if (value !== undefined) {
return value.toString();
}
}
if (checkThatExpressionIsProperSymbolReference(indexExpression, indexType, /*reportError*/ false)) {
const rightHandSideName = (<Identifier>(<PropertyAccessExpression>indexExpression).name).text;
return getPropertyNameForKnownSymbolName(rightHandSideName);
}
return undefined;
}
/**
* A proper symbol reference requires the following:
* 1. The property access denotes a property that exists
* 2. The expression is of the form Symbol.<identifier>
* 3. The property access is of the primitive type symbol.
* 4. Symbol in this context resolves to the global Symbol object
*/
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
if (expressionType === unknownType) {
// There is already an error, so no need to report one.