From 896a2b49ac0a36f4093355f0011bf7232c5845e0 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Wed, 24 Feb 2021 20:53:27 +0200 Subject: [PATCH] fix(42923): add go-to-definition on unresolved shorthand properties (#42924) --- src/services/goToDefinition.ts | 43 ++++++++++--------- .../goToDefinitionShorthandProperty04.ts | 11 +++++ .../goToDefinitionShorthandProperty05.ts | 11 +++++ .../goToDefinitionShorthandProperty06.ts | 11 +++++ 4 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 tests/cases/fourslash/goToDefinitionShorthandProperty04.ts create mode 100644 tests/cases/fourslash/goToDefinitionShorthandProperty05.ts create mode 100644 tests/cases/fourslash/goToDefinitionShorthandProperty06.ts diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 33330d1d0b0..e1ea158f138 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -51,7 +51,8 @@ namespace ts.GoToDefinition { // assignment. This case and others are handled by the following code. if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); - return shorthandSymbol ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : []; + const definitions = shorthandSymbol ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : emptyArray; + return concatenate(definitions, getDefinitionFromObjectLiteralElement(typeChecker, node) || emptyArray); } // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the @@ -75,25 +76,7 @@ namespace ts.GoToDefinition { }); } - // If the current location we want to find its definition is in an object literal, try to get the contextual type for the - // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. - // For example - // interface Props{ - // /*first*/prop1: number - // prop2: boolean - // } - // function Foo(arg: Props) {} - // Foo( { pr/*1*/op1: 10, prop2: true }) - const element = getContainingObjectLiteralElement(node); - if (element) { - const contextualType = element && typeChecker.getContextualType(element.parent); - if (contextualType) { - return flatMap(getPropertySymbolsFromContextualType(element, typeChecker, contextualType, /*unionSymbolOk*/ false), propertySymbol => - getDefinitionFromSymbol(typeChecker, propertySymbol, node)); - } - } - - return getDefinitionFromSymbol(typeChecker, symbol, node); + return getDefinitionFromObjectLiteralElement(typeChecker, node) || getDefinitionFromSymbol(typeChecker, symbol, node); } /** @@ -108,6 +91,26 @@ namespace ts.GoToDefinition { || (!isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol); } + // If the current location we want to find its definition is in an object literal, try to get the contextual type for the + // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. + // For example + // interface Props{ + // /*first*/prop1: number + // prop2: boolean + // } + // function Foo(arg: Props) {} + // Foo( { pr/*1*/op1: 10, prop2: true }) + function getDefinitionFromObjectLiteralElement(typeChecker: TypeChecker, node: Node) { + const element = getContainingObjectLiteralElement(node); + if (element) { + const contextualType = element && typeChecker.getContextualType(element.parent); + if (contextualType) { + return flatMap(getPropertySymbolsFromContextualType(element, typeChecker, contextualType, /*unionSymbolOk*/ false), propertySymbol => + getDefinitionFromSymbol(typeChecker, propertySymbol, node)); + } + } + } + export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { reference: FileReference, file: SourceFile } | undefined { const referencePath = findReferenceInPosition(sourceFile.referencedFiles, position); if (referencePath) { diff --git a/tests/cases/fourslash/goToDefinitionShorthandProperty04.ts b/tests/cases/fourslash/goToDefinitionShorthandProperty04.ts new file mode 100644 index 00000000000..4f24a9aef76 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionShorthandProperty04.ts @@ -0,0 +1,11 @@ +/// + +////interface Foo { +//// /*2*/foo(): void +////} +//// +////let x: Foo = { +//// [|f/*1*/oo|] +////} + +verify.goToDefinition("1", "2"); diff --git a/tests/cases/fourslash/goToDefinitionShorthandProperty05.ts b/tests/cases/fourslash/goToDefinitionShorthandProperty05.ts new file mode 100644 index 00000000000..d00036ca381 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionShorthandProperty05.ts @@ -0,0 +1,11 @@ +/// + +////interface Foo { +//// /*3*/foo(): void +////} +////const /*2*/foo = 1; +////let x: Foo = { +//// [|f/*1*/oo|] +////} + +verify.goToDefinition("1", ["2", "3"]); diff --git a/tests/cases/fourslash/goToDefinitionShorthandProperty06.ts b/tests/cases/fourslash/goToDefinitionShorthandProperty06.ts new file mode 100644 index 00000000000..8fe36acdff7 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionShorthandProperty06.ts @@ -0,0 +1,11 @@ +/// + +////interface Foo { +//// /*2*/foo(): void +////} +////const foo = 1; +////let x: Foo = { +//// [|f/*1*/oo|]() +////} + +verify.goToDefinition("1", "2");