From 82169ce7eb2e31c027fd2c9c37883faedb207c3c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 27 Feb 2016 18:12:40 -0800 Subject: [PATCH] Fix getTypeOfSymbolAtLocation to handle hypothetical lookups --- src/compiler/checker.ts | 34 ++++++++++++++++++++++++++++++---- src/compiler/types.ts | 5 +++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cd4a21204a2..b3be9545848 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6863,7 +6863,19 @@ namespace ts { // EXPRESSION TYPE CHECKING + function createTransientIdentifier(symbol: Symbol, location: Node): Identifier { + let result = createNode(SyntaxKind.Identifier); + result.text = symbol.name; + result.resolvedSymbol = symbol; + result.parent = location; + result.id = -1; + return result; + } + function getResolvedSymbol(node: Identifier): Symbol { + if (node.id === -1) { + return (node).resolvedSymbol; + } const links = getNodeLinks(node); if (!links.resolvedSymbol) { links.resolvedSymbol = !nodeIsMissing(node) && resolveName(node, node.text, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node) || unknownSymbol; @@ -7356,11 +7368,25 @@ namespace ts { function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) { // The language service will always care about the narrowed type of a symbol, because that is // the type the language says the symbol should have. - let type = getTypeOfSymbol(symbol); - if (location.kind === SyntaxKind.Identifier && isExpression(location) && getResolvedSymbol(location) === symbol) { - type = getNarrowedTypeOfReference(type, location); + const type = getTypeOfSymbol(symbol); + if (location.kind === SyntaxKind.Identifier) { + if (isRightSideOfQualifiedNameOrPropertyAccess(location)) { + location = location.parent; + } + // If location is an identifier or property access that references the given + // symbol, use the location as the reference with respect to which we narrow. + if (isExpression(location)) { + checkExpression(location); + if (getNodeLinks(location).resolvedSymbol === symbol) { + return getNarrowedTypeOfReference(type, location); + } + } } - return type; + // The location isn't a reference to the given symbol, meaning we're being asked + // a hypothetical question of what type the symbol would have if there was a reference + // to it at the given location. To answer that question we manufacture a transient + // identifier at the location and narrow with respect to that identifier. + return getNarrowedTypeOfReference(type, createTransientIdentifier(symbol, location)); } function skipParenthesizedNodes(expression: Expression): Expression { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b4b81c2732f..840d7988405 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -476,6 +476,11 @@ namespace ts { originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later } + // Transient identifier node (marked by id === -1) + export interface TransientIdentifier extends Identifier { + resolvedSymbol: Symbol; + } + // @kind(SyntaxKind.QualifiedName) export interface QualifiedName extends Node { // Must have same layout as PropertyAccess