|
1 | 1 | /* @internal */
|
2 | 2 | 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 { |
4 | 4 | const reference = getReferenceAtPosition(sourceFile, position, program);
|
5 | 5 | if (reference) {
|
6 | 6 | return [getDefinitionInfoForFileReference(reference.fileName, reference.file.fileName)];
|
@@ -29,7 +29,7 @@ namespace ts.GoToDefinition {
|
29 | 29 | // Could not find a symbol e.g. node is string or number keyword,
|
30 | 30 | // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
|
31 | 31 | if (!symbol) {
|
32 |
| - return undefined; |
| 32 | + return getDefinitionInfoForIndexSignatures(node, typeChecker); |
33 | 33 | }
|
34 | 34 |
|
35 | 35 | // If this is an alias, and the request came at the declaration location
|
@@ -157,6 +157,29 @@ namespace ts.GoToDefinition {
|
157 | 157 | return { definitions, textSpan };
|
158 | 158 | }
|
159 | 159 |
|
| 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 | + |
160 | 183 | // Go to the original declaration for cases:
|
161 | 184 | //
|
162 | 185 | // (1) when the aliased symbol was declared in the location(parent).
|
|
0 commit comments