diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e22ab8affea01..a206526e43570 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14381,11 +14381,12 @@ namespace ts { return getTypeFromInferTypeNode(node); case SyntaxKind.ImportType: return getTypeFromImportTypeNode(node); - // This function assumes that an identifier or qualified name is a type expression + // This function assumes that an identifier, qualified name, or property access expression is a type expression // Callers should first ensure this by calling `isPartOfTypeNode` // TODO(rbuckton): These aren't valid TypeNodes, but we treat them as such because of `isPartOfTypeNode`, which returns `true` for things that aren't `TypeNode`s. case SyntaxKind.Identifier as TypeNodeSyntaxKind: case SyntaxKind.QualifiedName as TypeNodeSyntaxKind: + case SyntaxKind.PropertyAccessExpression as TypeNodeSyntaxKind: const symbol = getSymbolAtLocation(node); return symbol ? getDeclaredTypeOfSymbol(symbol) : errorType; default: diff --git a/src/testRunner/unittests/publicApi.ts b/src/testRunner/unittests/publicApi.ts index 7104662d033ef..cbb7e1b536a38 100644 --- a/src/testRunner/unittests/publicApi.ts +++ b/src/testRunner/unittests/publicApi.ts @@ -77,3 +77,31 @@ describe("unittests:: Public APIs:: isPropertyName", () => { assert.isTrue(ts.isPropertyName(prop), "PrivateIdentifier must be a valid property name."); }); }); + +describe("unittests:: Public APIs:: getTypeAtLocation", () => { + it("works on PropertyAccessExpression in implements clause", () => { + const content = `namespace Test { + export interface Test {} + } + class Foo implements Test.Test {}`; + + const host = new fakes.CompilerHost(vfs.createFromFileSystem( + Harness.IO, + /*ignoreCase*/ true, + { documents: [new documents.TextDocument("/file.ts", content)], cwd: "/" })); + + const program = ts.createProgram({ + host, + rootNames: ["/file.ts"], + options: { noLib: true } + }); + + const checker = program.getTypeChecker(); + const file = program.getSourceFile("/file.ts")!; + const classDeclaration = file.statements.find(ts.isClassDeclaration)!; + const propertyAccess = classDeclaration.heritageClauses![0].types[0].expression as ts.PropertyAccessExpression; + const type = checker.getTypeAtLocation(propertyAccess); + assert.ok(!(type.flags & ts.TypeFlags.Any)); + assert.equal(type, checker.getTypeAtLocation(propertyAccess.name)); + }); +});