From 41c20545e1891e9b1359e107e67dff666815581f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 1 Nov 2016 10:17:08 -0700 Subject: [PATCH] Improve unification by moving more logic to getIndexedAccessType --- src/compiler/checker.ts | 73 +++++++++++------------------------------ 1 file changed, 19 insertions(+), 54 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2feec887cc1..31aab95ffb1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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 ? accessNode : undefined; - if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) { - const prop = getPropertyOfType(objectType, escapeIdentifier((indexType).text)); + const propName = indexType.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.EnumLiteral) ? + (indexType).text : + accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ? + getPropertyNameForKnownSymbolName(((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, indexType); } + const apparentType = getApparentType(objectType); if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) { const propTypes: Type[] = []; for (const t of (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(node, prop) || isReferenceThroughNamespaceImport(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 (indexExpression).text; - } - if (indexExpression.kind === SyntaxKind.ElementAccessExpression || indexExpression.kind === SyntaxKind.PropertyAccessExpression) { - const value = getConstantValue(indexExpression); - if (value !== undefined) { - return value.toString(); - } - } - if (checkThatExpressionIsProperSymbolReference(indexExpression, indexType, /*reportError*/ false)) { - const rightHandSideName = ((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. - * 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.