diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts
index daafc490573..4b39a90c880 100644
--- a/src/services/goToDefinition.ts
+++ b/src/services/goToDefinition.ts
@@ -100,10 +100,13 @@ namespace ts.GoToDefinition {
/**
* True if we should not add definitions for both the signature symbol and the definition symbol.
* True for `const |f = |() => 0`, false for `function |f() {} const |g = f;`.
+ * Also true for any assignment RHS.
*/
function symbolMatchesSignature(s: Symbol, calledDeclaration: SignatureDeclaration) {
- return s === calledDeclaration.symbol || s === calledDeclaration.symbol.parent ||
- !isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol;
+ return s === calledDeclaration.symbol
+ || s === calledDeclaration.symbol.parent
+ || isAssignmentExpression(calledDeclaration.parent)
+ || (!isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol);
}
export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { fileName: string, file: SourceFile } | undefined {
@@ -246,7 +249,9 @@ namespace ts.GoToDefinition {
// There are cases when you extend a function by adding properties to it afterwards,
// we want to strip those extra properties.
// For deduping purposes, we also want to exclude any declarationNodes if provided.
- const filteredDeclarations = filter(symbol.declarations, d => d !== declarationNode && (!isAssignmentDeclaration(d) || d === symbol.valueDeclaration)) || undefined;
+ const filteredDeclarations =
+ filter(symbol.declarations, d => d !== declarationNode && (!isAssignmentDeclaration(d) || d === symbol.valueDeclaration))
+ || undefined;
return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(filteredDeclarations, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node));
function getConstructSignatureDefinition(): DefinitionInfo[] | undefined {
@@ -330,15 +335,11 @@ namespace ts.GoToDefinition {
/** Returns a CallLikeExpression where `node` is the target being invoked. */
function getAncestorCallLikeExpression(node: Node): CallLikeExpression | undefined {
- const target = climbPastManyPropertyAccesses(node);
- const callLike = target.parent;
+ const target = findAncestor(node, n => !isRightSideOfPropertyAccess(n));
+ const callLike = target?.parent;
return callLike && isCallLikeExpression(callLike) && getInvokedExpression(callLike) === target ? callLike : undefined;
}
- function climbPastManyPropertyAccesses(node: Node): Node {
- return isRightSideOfPropertyAccess(node) ? climbPastManyPropertyAccesses(node.parent) : node;
- }
-
function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined {
const callLike = getAncestorCallLikeExpression(node);
const signature = callLike && typeChecker.getResolvedSignature(callLike);
diff --git a/tests/cases/fourslash/goToDefinitionJsModuleExports.ts b/tests/cases/fourslash/goToDefinitionJsModuleExports.ts
new file mode 100644
index 00000000000..43d81085a47
--- /dev/null
+++ b/tests/cases/fourslash/goToDefinitionJsModuleExports.ts
@@ -0,0 +1,10 @@
+///
+
+// #33520
+
+// @allowJs: true
+// @Filename: foo.js
+////x.test = /*def*/() => { }
+////x.[|/*ref*/test|]();
+
+verify.goToDefinition("ref", "def");
diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment.ts
index 09d5f1126f1..3b0c14655e7 100644
--- a/tests/cases/fourslash/goToDefinitionVariableAssignment.ts
+++ b/tests/cases/fourslash/goToDefinitionVariableAssignment.ts
@@ -4,9 +4,9 @@
// @checkJs: true
// @filename: foo.js
////const Bar;
-////const /*def1*/Foo = /*def2*/Bar = function () {}
+////const Foo = /*def*/Bar = function () {}
////Foo.prototype.bar = function() {}
////new [|Foo/*ref*/|]();
goTo.file("foo.js");
-verify.goToDefinition("ref", ["def1", "def2"]);
+verify.goToDefinition("ref", "def");
diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts
index 45e666cafa6..cce4afa072a 100644
--- a/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts
+++ b/tests/cases/fourslash/goToDefinitionVariableAssignment1.ts
@@ -3,9 +3,9 @@
// @allowJs: true
// @checkJs: true
// @filename: foo.js
-////const /*def1*/Foo = module./*def2*/exports = function () {}
+////const Foo = module./*def*/exports = function () {}
////Foo.prototype.bar = function() {}
////new [|Foo/*ref*/|]();
goTo.file("foo.js");
-verify.goToDefinition("ref", ["def1", "def2"]);
+verify.goToDefinition("ref", "def");
diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts
index d5474cda93c..a23c121a340 100644
--- a/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts
+++ b/tests/cases/fourslash/goToDefinitionVariableAssignment2.ts
@@ -2,9 +2,9 @@
// @filename: foo.ts
////const Bar;
-////const /*def1*/Foo = /*def2*/Bar = function () {}
+////const Foo = /*def*/Bar = function () {}
////Foo.prototype.bar = function() {}
////new [|Foo/*ref*/|]();
goTo.file("foo.ts");
-verify.goToDefinition("ref", ["def1", "def2"]);
+verify.goToDefinition("ref", "def");
diff --git a/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts b/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts
index 85d6c43e729..e1ac6b9c8ee 100644
--- a/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts
+++ b/tests/cases/fourslash/goToDefinitionVariableAssignment3.ts
@@ -1,9 +1,9 @@
///
// @filename: foo.ts
-////const /*def1*/Foo = module./*def2*/exports = function () {}
+////const Foo = module./*def*/exports = function () {}
////Foo.prototype.bar = function() {}
////new [|Foo/*ref*/|]();
goTo.file("foo.ts");
-verify.goToDefinition("ref", ["def1", "def2"]);
+verify.goToDefinition("ref", "def");