From 998beadba2fa946fa66baa407ddb01c4ae4839df Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 9 Apr 2018 12:48:41 -0700 Subject: [PATCH] Support goto-definition for index signatures (#23220) * Support goto-definition for index signatures * Use checker.getIndexInfoOfType * Handle undefined info.declaration --- src/services/goToDefinition.ts | 14 ++++++++++++-- .../fourslash/goToDefinitionIndexSignature.ts | 15 +++++++++++++++ .../fourslash/goToDefinitionIndexSignature2.ts | 11 +++++++++++ tests/cases/fourslash/goToDefinitionRest.ts | 2 +- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/goToDefinitionIndexSignature.ts create mode 100644 tests/cases/fourslash/goToDefinitionIndexSignature2.ts diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 4b2ba7e4536..226fe74ef10 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -1,6 +1,6 @@ /* @internal */ namespace ts.GoToDefinition { - export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] { + export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] | undefined { const reference = getReferenceAtPosition(sourceFile, position, program); if (reference) { return [getDefinitionInfoForFileReference(reference.fileName, reference.file.fileName)]; @@ -29,7 +29,7 @@ namespace ts.GoToDefinition { // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol if (!symbol) { - return undefined; + return getDefinitionInfoForIndexSignatures(node, typeChecker); } // If this is an alias, and the request came at the declaration location @@ -157,6 +157,16 @@ namespace ts.GoToDefinition { return { definitions, textSpan }; } + // At 'x.foo', see if the type of 'x' has an index signature, and if so find its declarations. + function getDefinitionInfoForIndexSignatures(node: Node, checker: TypeChecker): DefinitionInfo[] | undefined { + if (!isPropertyAccessExpression(node.parent) || node.parent.name !== node) return; + const type = checker.getTypeAtLocation(node.parent.expression); + return mapDefined(type.isUnionOrIntersection() ? type.types : [type], nonUnionType => { + const info = checker.getIndexInfoOfType(nonUnionType, IndexKind.String); + return info && info.declaration && createDefinitionFromSignatureDeclaration(checker, info.declaration); + }); + } + // Go to the original declaration for cases: // // (1) when the aliased symbol was declared in the location(parent). diff --git a/tests/cases/fourslash/goToDefinitionIndexSignature.ts b/tests/cases/fourslash/goToDefinitionIndexSignature.ts new file mode 100644 index 00000000000..d5438830dc0 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionIndexSignature.ts @@ -0,0 +1,15 @@ +/// + +////interface I { +//// /*defI*/[x: string]: boolean; +////} +////interface J { +//// /*defJ*/[x: string]: number; +////} +////declare const i: I; +////i.[|/*useI*/foo|]; +////declare const ij: I | J; +////ij.[|/*useIJ*/foo|]; + +verify.goToDefinition("useI", ["defI"]); +verify.goToDefinition("useIJ", ["defI", "defJ"]); diff --git a/tests/cases/fourslash/goToDefinitionIndexSignature2.ts b/tests/cases/fourslash/goToDefinitionIndexSignature2.ts new file mode 100644 index 00000000000..9188eaf67d0 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionIndexSignature2.ts @@ -0,0 +1,11 @@ +/// + +// Tests that we don't crash for an index signature with no declaration. + +// @allowJs: true + +// @Filename: /a.js +////const o = {}; +////o.[|/*use*/foo|]; + +verify.goToDefinition("use", []); diff --git a/tests/cases/fourslash/goToDefinitionRest.ts b/tests/cases/fourslash/goToDefinitionRest.ts index 2577aafb625..4f1aa23c2ad 100644 --- a/tests/cases/fourslash/goToDefinitionRest.ts +++ b/tests/cases/fourslash/goToDefinitionRest.ts @@ -8,5 +8,5 @@ ////let t: Gen; ////var { x, ...rest } = t; ////rest.[|/*2*/parent|]; -const ranges = test.ranges(); + verify.goToDefinition('2', [ '1' ]);