Skip to content

Commit a716faa

Browse files
author
Andy Hanson
committed
Support goto-definition for index signatures
1 parent 05cd784 commit a716faa

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

src/services/goToDefinition.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* @internal */
22
namespace ts.GoToDefinition {
3-
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] {
3+
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] | undefined {
44
const reference = getReferenceAtPosition(sourceFile, position, program);
55
if (reference) {
66
return [getDefinitionInfoForFileReference(reference.fileName, reference.file.fileName)];
@@ -29,7 +29,7 @@ namespace ts.GoToDefinition {
2929
// Could not find a symbol e.g. node is string or number keyword,
3030
// or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
3131
if (!symbol) {
32-
return undefined;
32+
return getDefinitionInfoForIndexSignatures(node, typeChecker);
3333
}
3434

3535
// If this is an alias, and the request came at the declaration location
@@ -157,6 +157,29 @@ namespace ts.GoToDefinition {
157157
return { definitions, textSpan };
158158
}
159159

160+
// At 'x.foo', see if the type of 'x' has an index signature, and if so find its declarations.
161+
function getDefinitionInfoForIndexSignatures(node: Node, checker: TypeChecker): DefinitionInfo[] | undefined {
162+
if (!isPropertyAccessExpression(node.parent) || node.parent.name !== node) return;
163+
const type = checker.getTypeAtLocation(node.parent.expression);
164+
if (!type.getStringIndexType()) return undefined;
165+
const result: DefinitionInfo[] = [];
166+
for (const root of getRootSymbolsOfType(type, checker)) {
167+
for (const decl of root.declarations) {
168+
if (!isObjectTypeDeclaration(decl)) continue;
169+
for (const member of decl.members) {
170+
if (isIndexSignatureDeclaration(member)) {
171+
result.push(createDefinitionFromSignatureDeclaration(checker, member));
172+
}
173+
}
174+
}
175+
}
176+
return result;
177+
}
178+
179+
function getRootSymbolsOfType(type: Type, checker: TypeChecker): ReadonlyArray<Symbol> {
180+
return flatMap(type.isUnionOrIntersection() ? type.types : [type], t => t.symbol ? checker.getRootSymbols(t.symbol) : emptyArray);
181+
}
182+
160183
// Go to the original declaration for cases:
161184
//
162185
// (1) when the aliased symbol was declared in the location(parent).
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////interface I {
4+
//// /*defI*/[x: string]: boolean;
5+
////}
6+
////interface J {
7+
//// /*defJ*/[x: string]: number;
8+
////}
9+
////declare const i: I;
10+
////i.[|/*useI*/foo|];
11+
////declare const ij: I | J;
12+
////ij.[|/*useIJ*/foo|];
13+
14+
verify.goToDefinition("useI", ["defI"]);
15+
verify.goToDefinition("useIJ", ["defI", "defJ"]);

tests/cases/fourslash/goToDefinitionRest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
////let t: Gen;
99
////var { x, ...rest } = t;
1010
////rest.[|/*2*/parent|];
11-
const ranges = test.ranges();
11+
1212
verify.goToDefinition('2', [ '1' ]);

0 commit comments

Comments
 (0)