From a8230d3cb9eb02af368566805cd27cffbecf525b Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 25 Jul 2018 17:17:52 -0700 Subject: [PATCH 1/3] goToTypeDefinition: Go to function return type --- src/harness/fourslash.ts | 2 +- src/services/goToDefinition.ts | 17 +++++++++++++++- .../goToTypeDefinition_returnType.ts | 20 +++++++++++++++++++ .../declarationMapsEnableMapping_NoInline.ts | 14 ++++++------- ...rationMapsEnableMapping_NoInlineSources.ts | 14 ++++++------- ...clarationMapsGeneratedMapsEnableMapping.ts | 12 +++++------ ...larationMapsGeneratedMapsEnableMapping2.ts | 14 ++++++------- ...larationMapsGeneratedMapsEnableMapping3.ts | 12 +++++------ 8 files changed, 70 insertions(+), 35 deletions(-) create mode 100644 tests/cases/fourslash/goToTypeDefinition_returnType.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 6d50a66ad49df..6efcfc5519cbc 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1674,7 +1674,7 @@ Actual: ${stringify(fullActual)}`); for (const { name, text } of outputFiles) { const fromTestFile = this.getFileContent(name); if (fromTestFile !== text) { - this.raiseError("Emit output is not as expected: " + showTextDiff(fromTestFile, text)); + this.raiseError(`Emit output for ${name} is not as expected: ${showTextDiff(fromTestFile, text)}`); } } } diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 799140097cd95..0c715fae7b0b3 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -135,11 +135,26 @@ namespace ts.GoToDefinition { } const symbol = typeChecker.getSymbolAtLocation(node); - const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, node); + const type = symbol && getTypeForTypeDefinition(symbol, node, typeChecker); return type && flatMap(type.isUnion() && !(type.flags & TypeFlags.Enum) ? type.types : [type], t => t.symbol && getDefinitionFromSymbol(typeChecker, t.symbol, node)); } + function getTypeForTypeDefinition(symbol: Symbol, node: Node, checker: TypeChecker): Type | undefined { + const type = checker.getTypeOfSymbolAtLocation(symbol, node); + if (!type) return undefined; + + // If the type is just a function's inferred type, + // go-to-type should go to the return type instead, since go-to-definition takes you to the function anyway. + if (type.symbol === symbol || + // At `const f = () => {}`, the symbol is `f` and the type symbol is at `() => {}` + symbol.valueDeclaration && type.symbol && isVariableDeclaration(symbol.valueDeclaration) && symbol.valueDeclaration.initializer === type.symbol.valueDeclaration as Node) { + const sigs = type.getCallSignatures(); + if (sigs.length === 1) return checker.getReturnTypeOfSignature(first(sigs)); + } + return type; + } + export function getDefinitionAndBoundSpan(program: Program, sourceFile: SourceFile, position: number): DefinitionInfoAndBoundSpan | undefined { const definitions = getDefinitionAtPosition(program, sourceFile, position); diff --git a/tests/cases/fourslash/goToTypeDefinition_returnType.ts b/tests/cases/fourslash/goToTypeDefinition_returnType.ts new file mode 100644 index 0000000000000..e5d06eea5f909 --- /dev/null +++ b/tests/cases/fourslash/goToTypeDefinition_returnType.ts @@ -0,0 +1,20 @@ +/// + +////interface /*I*/I { x: number; } +//// +////function f0(): I { return { x: 0 }; } +//// +////type T = /*T*/(i: I) => I; +////const f1: T = i => ({ x: i.x + 1 }); +//// +////const f2 = (i: I): I => ({ x: i.x + 1 }); +//// +/////*f0*/f0(); +/////*f1*/f1(); +/////*f2*/f2(); + +verify.goToType({ + f0: "I", + f1: "T", + f2: "I", +}); diff --git a/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInline.ts b/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInline.ts index f85993e708452..09ebebdc0c6a1 100644 --- a/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInline.ts +++ b/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInline.ts @@ -16,7 +16,7 @@ // @emitThisFile: true ////export class Foo { //// member: string; -//// /*2*/methodName(propName: SomeType): void {} +//// /*2*/methodName(propName: SomeType): SomeType { return propName; } //// otherMethod() { //// if (Math.random() > 0.5) { //// return {x: 42}; @@ -25,7 +25,7 @@ //// } ////} //// -////export interface SomeType { +////export interface /*SomeType*/SomeType { //// member: number; ////} @@ -40,7 +40,7 @@ ////var Foo = /** @class */ (function () { //// function Foo() { //// } -//// Foo.prototype.methodName = function (propName) { }; +//// Foo.prototype.methodName = function (propName) { return propName; }; //// Foo.prototype.otherMethod = function () { //// if (Math.random() > 0.5) { //// return { x: 42 }; @@ -50,15 +50,15 @@ //// return Foo; ////}()); ////exports.Foo = Foo; -//////# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBO0lBQUE7SUFTQSxDQUFDO0lBUEcsd0JBQVUsR0FBVixVQUFXLFFBQWtCLElBQVMsQ0FBQztJQUN2Qyx5QkFBVyxHQUFYO1FBQ0ksSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxFQUFFO1lBQ3JCLE9BQU8sRUFBQyxDQUFDLEVBQUUsRUFBRSxFQUFDLENBQUM7U0FDbEI7UUFDRCxPQUFPLEVBQUMsQ0FBQyxFQUFFLEtBQUssRUFBQyxDQUFDO0lBQ3RCLENBQUM7SUFDTCxVQUFDO0FBQUQsQ0FBQyxBQVRELElBU0M7QUFUWSxrQkFBRyJ9 +//////# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBO0lBQUE7SUFTQSxDQUFDO0lBUEcsd0JBQVUsR0FBVixVQUFXLFFBQWtCLElBQWMsT0FBTyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQzdELHlCQUFXLEdBQVg7UUFDSSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLEVBQUU7WUFDckIsT0FBTyxFQUFDLENBQUMsRUFBRSxFQUFFLEVBQUMsQ0FBQztTQUNsQjtRQUNELE9BQU8sRUFBQyxDQUFDLEVBQUUsS0FBSyxFQUFDLENBQUM7SUFDdEIsQ0FBQztJQUNMLFVBQUM7QUFBRCxDQUFDLEFBVEQsSUFTQztBQVRZLGtCQUFHIn0= // @Filename: /dist/index.d.ts.map -////{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IACpC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} +////{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ;IACxC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} // @Filename: /dist/index.d.ts ////export declare class Foo { //// member: string; -//// methodName(propName: SomeType): void; +//// methodName(propName: SomeType): SomeType; //// otherMethod(): { //// x: number; //// y?: undefined; @@ -76,7 +76,7 @@ goTo.file("/index.ts"); verify.getEmitOutput(["/dist/index.js", "/dist/index.d.ts.map", "/dist/index.d.ts"]); verify.goToDefinition("1", "2"); // getDefinitionAndBoundSpan -verify.goToType("1", "2"); // getTypeDefinitionAtPosition +verify.goToType("1", "SomeType"); // getTypeDefinitionAtPosition goTo.marker("1"); verify.goToDefinitionIs("2"); // getDefinitionAtPosition goTo.implementation(); // getImplementationAtPosition diff --git a/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInlineSources.ts b/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInlineSources.ts index 83ef95f2f4ac2..3a371646bfb88 100644 --- a/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInlineSources.ts +++ b/tests/cases/fourslash/server/declarationMapsEnableMapping_NoInlineSources.ts @@ -17,7 +17,7 @@ // @emitThisFile: true ////export class Foo { //// member: string; -//// /*2*/methodName(propName: SomeType): void {} +//// /*2*/methodName(propName: SomeType): SomeType { return propName; } //// otherMethod() { //// if (Math.random() > 0.5) { //// return {x: 42}; @@ -26,7 +26,7 @@ //// } ////} //// -////export interface SomeType { +////export interface /*SomeType*/SomeType { //// member: number; ////} @@ -41,7 +41,7 @@ ////var Foo = /** @class */ (function () { //// function Foo() { //// } -//// Foo.prototype.methodName = function (propName) { }; +//// Foo.prototype.methodName = function (propName) { return propName; }; //// Foo.prototype.otherMethod = function () { //// if (Math.random() > 0.5) { //// return { x: 42 }; @@ -51,15 +51,15 @@ //// return Foo; ////}()); ////exports.Foo = Foo; -//////# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBO0lBQUE7SUFTQSxDQUFDO0lBUEcsd0JBQVUsR0FBVixVQUFXLFFBQWtCLElBQVMsQ0FBQztJQUN2Qyx5QkFBVyxHQUFYO1FBQ0ksSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxFQUFFO1lBQ3JCLE9BQU8sRUFBQyxDQUFDLEVBQUUsRUFBRSxFQUFDLENBQUM7U0FDbEI7UUFDRCxPQUFPLEVBQUMsQ0FBQyxFQUFFLEtBQUssRUFBQyxDQUFDO0lBQ3RCLENBQUM7SUFDTCxVQUFDO0FBQUQsQ0FBQyxBQVRELElBU0M7QUFUWSxrQkFBRyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBjbGFzcyBGb28ge1xuICAgIG1lbWJlcjogc3RyaW5nO1xuICAgIG1ldGhvZE5hbWUocHJvcE5hbWU6IFNvbWVUeXBlKTogdm9pZCB7fVxuICAgIG90aGVyTWV0aG9kKCkge1xuICAgICAgICBpZiAoTWF0aC5yYW5kb20oKSA+IDAuNSkge1xuICAgICAgICAgICAgcmV0dXJuIHt4OiA0Mn07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHt5OiBcInllc1wifTtcbiAgICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU29tZVR5cGUge1xuICAgIG1lbWJlcjogbnVtYmVyO1xufSJdfQ== +//////# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBO0lBQUE7SUFTQSxDQUFDO0lBUEcsd0JBQVUsR0FBVixVQUFXLFFBQWtCLElBQWMsT0FBTyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQzdELHlCQUFXLEdBQVg7UUFDSSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxHQUFHLEVBQUU7WUFDckIsT0FBTyxFQUFDLENBQUMsRUFBRSxFQUFFLEVBQUMsQ0FBQztTQUNsQjtRQUNELE9BQU8sRUFBQyxDQUFDLEVBQUUsS0FBSyxFQUFDLENBQUM7SUFDdEIsQ0FBQztJQUNMLFVBQUM7QUFBRCxDQUFDLEFBVEQsSUFTQztBQVRZLGtCQUFHIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGNsYXNzIEZvbyB7XG4gICAgbWVtYmVyOiBzdHJpbmc7XG4gICAgbWV0aG9kTmFtZShwcm9wTmFtZTogU29tZVR5cGUpOiBTb21lVHlwZSB7IHJldHVybiBwcm9wTmFtZTsgfVxuICAgIG90aGVyTWV0aG9kKCkge1xuICAgICAgICBpZiAoTWF0aC5yYW5kb20oKSA+IDAuNSkge1xuICAgICAgICAgICAgcmV0dXJuIHt4OiA0Mn07XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHt5OiBcInllc1wifTtcbiAgICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU29tZVR5cGUge1xuICAgIG1lbWJlcjogbnVtYmVyO1xufSJdfQ== // @Filename: /dist/index.d.ts.map -////{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IACpC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} +////{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ;IACxC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} // @Filename: /dist/index.d.ts ////export declare class Foo { //// member: string; -//// methodName(propName: SomeType): void; +//// methodName(propName: SomeType): SomeType; //// otherMethod(): { //// x: number; //// y?: undefined; @@ -77,7 +77,7 @@ goTo.file("/index.ts"); verify.getEmitOutput(["/dist/index.js", "/dist/index.d.ts.map", "/dist/index.d.ts"]); verify.goToDefinition("1", "2"); // getDefinitionAndBoundSpan -verify.goToType("1", "2"); // getTypeDefinitionAtPosition +verify.goToType("1", "SomeType"); // getTypeDefinitionAtPosition goTo.marker("1"); verify.goToDefinitionIs("2"); // getDefinitionAtPosition goTo.implementation(); // getImplementationAtPosition diff --git a/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping.ts b/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping.ts index c8ea0f74a4391..1cfefa3878293 100644 --- a/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping.ts +++ b/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping.ts @@ -15,7 +15,7 @@ // @emitThisFile: true ////export class Foo { //// member: string; -//// /*2*/methodName(propName: SomeType): void {} +//// /*2*/methodName(propName: SomeType): SomeType { return propName; } //// otherMethod() { //// if (Math.random() > 0.5) { //// return {x: 42}; @@ -24,7 +24,7 @@ //// } ////} //// -////export interface SomeType { +////export interface /*SomeType*/SomeType { //// member: number; ////} @@ -39,7 +39,7 @@ ////var Foo = /** @class */ (function () { //// function Foo() { //// } -//// Foo.prototype.methodName = function (propName) { }; +//// Foo.prototype.methodName = function (propName) { return propName; }; //// Foo.prototype.otherMethod = function () { //// if (Math.random() > 0.5) { //// return { x: 42 }; @@ -52,12 +52,12 @@ //// // @Filename: /dist/index.d.ts.map -////{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IACpC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} +////{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ;IACxC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} // @Filename: /dist/index.d.ts ////export declare class Foo { //// member: string; -//// methodName(propName: SomeType): void; +//// methodName(propName: SomeType): SomeType; //// otherMethod(): { //// x: number; //// y?: undefined; @@ -75,7 +75,7 @@ goTo.file("/index.ts"); verify.getEmitOutput(["/dist/index.js", "/dist/index.d.ts.map", "/dist/index.d.ts"]); verify.goToDefinition("1", "2"); // getDefinitionAndBoundSpan -verify.goToType("1", "2"); // getTypeDefinitionAtPosition +verify.goToType("1", "SomeType"); // getTypeDefinitionAtPosition goTo.marker("1"); verify.goToDefinitionIs("2"); // getDefinitionAtPosition goTo.implementation(); // getImplementationAtPosition diff --git a/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping2.ts b/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping2.ts index 7799d61e2aeb2..94185d15bcd32 100644 --- a/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping2.ts +++ b/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping2.ts @@ -17,7 +17,7 @@ // @emitThisFile: true ////export class Foo { //// member: string; -//// /*2*/methodName(propName: SomeType): void {} +//// /*2*/methodName(propName: SomeType): SomeType { return propName; } //// otherMethod() { //// if (Math.random() > 0.5) { //// return {x: 42}; @@ -26,7 +26,7 @@ //// } ////} //// -////export interface SomeType { +////export interface /*SomeType*/SomeType { //// member: number; ////} @@ -36,7 +36,7 @@ ////instance.[|/*1*/methodName|]({member: 12}); // @Filename: /dist/index.js.map -////{"version":3,"file":"index.js","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":";;AAAA;IAAA;IASA,CAAC;IAPG,wBAAU,GAAV,UAAW,QAAkB,IAAS,CAAC;IACvC,yBAAW,GAAX;QACI,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,EAAE;YACrB,OAAO,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC;SAClB;QACD,OAAO,EAAC,CAAC,EAAE,KAAK,EAAC,CAAC;IACtB,CAAC;IACL,UAAC;AAAD,CAAC,AATD,IASC;AATY,kBAAG"} +////{"version":3,"file":"index.js","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":";;AAAA;IAAA;IASA,CAAC;IAPG,wBAAU,GAAV,UAAW,QAAkB,IAAc,OAAO,QAAQ,CAAC,CAAC,CAAC;IAC7D,yBAAW,GAAX;QACI,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,EAAE;YACrB,OAAO,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC;SAClB;QACD,OAAO,EAAC,CAAC,EAAE,KAAK,EAAC,CAAC;IACtB,CAAC;IACL,UAAC;AAAD,CAAC,AATD,IASC;AATY,kBAAG"} // @Filename: /dist/index.js ////"use strict"; @@ -44,7 +44,7 @@ ////var Foo = /** @class */ (function () { //// function Foo() { //// } -//// Foo.prototype.methodName = function (propName) { }; +//// Foo.prototype.methodName = function (propName) { return propName; }; //// Foo.prototype.otherMethod = function () { //// if (Math.random() > 0.5) { //// return { x: 42 }; @@ -57,12 +57,12 @@ //////# sourceMappingURL=index.js.map // @Filename: /dist/index.d.ts.map -////{"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IACpC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} +////{"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ;IACxC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} // @Filename: /dist/index.d.ts ////export declare class Foo { //// member: string; -//// methodName(propName: SomeType): void; +//// methodName(propName: SomeType): SomeType; //// otherMethod(): { //// x: number; //// y?: undefined; @@ -80,7 +80,7 @@ goTo.file("/index.ts"); verify.getEmitOutput(["/dist/index.js.map", "/dist/index.js", "/dist/index.d.ts.map", "/dist/index.d.ts"]); verify.goToDefinition("1", "2"); // getDefinitionAndBoundSpan -verify.goToType("1", "2"); // getTypeDefinitionAtPosition +verify.goToType("1", "SomeType"); // getTypeDefinitionAtPosition goTo.marker("1"); verify.goToDefinitionIs("2"); // getDefinitionAtPosition goTo.implementation(); // getImplementationAtPosition diff --git a/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping3.ts b/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping3.ts index 5785b595440d7..4215ffe25e6ec 100644 --- a/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping3.ts +++ b/tests/cases/fourslash/server/declarationMapsGeneratedMapsEnableMapping3.ts @@ -16,7 +16,7 @@ // @emitThisFile: true ////export class Foo { //// member: string; -//// /*2*/methodName(propName: SomeType): void {} +//// /*2*/methodName(propName: SomeType): SomeType { return propName; } //// otherMethod() { //// if (Math.random() > 0.5) { //// return {x: 42}; @@ -25,7 +25,7 @@ //// } ////} //// -////export interface SomeType { +////export interface /*SomeType*/SomeType { //// member: number; ////} @@ -40,7 +40,7 @@ ////var Foo = /** @class */ (function () { //// function Foo() { //// } -//// Foo.prototype.methodName = function (propName) { }; +//// Foo.prototype.methodName = function (propName) { return propName; }; //// Foo.prototype.otherMethod = function () { //// if (Math.random() > 0.5) { //// return { x: 42 }; @@ -53,12 +53,12 @@ //// // @Filename: /dist/index.d.ts.map -////{"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IACpC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} +////{"version":3,"file":"index.d.ts","sourceRoot":"/","sources":["index.ts"],"names":[],"mappings":"AAAA,qBAAa,GAAG;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ;IACxC,WAAW;;;;;;;CAMd;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;CAClB"} // @Filename: /dist/index.d.ts ////export declare class Foo { //// member: string; -//// methodName(propName: SomeType): void; +//// methodName(propName: SomeType): SomeType; //// otherMethod(): { //// x: number; //// y?: undefined; @@ -76,7 +76,7 @@ goTo.file("/index.ts"); verify.getEmitOutput(["/dist/index.js", "/dist/index.d.ts.map", "/dist/index.d.ts"]); verify.goToDefinition("1", "2"); // getDefinitionAndBoundSpan -verify.goToType("1", "2"); // getTypeDefinitionAtPosition +verify.goToType("1", "SomeType"); // getTypeDefinitionAtPosition goTo.marker("1"); verify.goToDefinitionIs("2"); // getDefinitionAtPosition goTo.implementation(); // getImplementationAtPosition From 320066b90c1c93f61a8006d1f30523afee11a0bb Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 26 Jul 2018 09:56:41 -0700 Subject: [PATCH 2/3] Add more tests --- .../goToTypeDefinition_returnType.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/cases/fourslash/goToTypeDefinition_returnType.ts b/tests/cases/fourslash/goToTypeDefinition_returnType.ts index e5d06eea5f909..1c404bb0e1132 100644 --- a/tests/cases/fourslash/goToTypeDefinition_returnType.ts +++ b/tests/cases/fourslash/goToTypeDefinition_returnType.ts @@ -1,6 +1,7 @@ /// ////interface /*I*/I { x: number; } +////interface /*J*/J { y: number; } //// ////function f0(): I { return { x: 0 }; } //// @@ -9,12 +10,38 @@ //// ////const f2 = (i: I): I => ({ x: i.x + 1 }); //// +////const f3 = (i: I) => (/*f3Def*/{ x: i.x + 1 }); +//// +////const f4 = (i: I) => i; +//// +////const f5 = /*f5Def*/(i: I): I | J => ({ x: i.x + 1 }); +//// +////const f6 = (i: I, j: J, b: boolean) => b ? i : j; +//// +////const f7 = /*f7Def*/(i: I) => {}; +//// +////function f8(i: I): I; +////function f8(j: J): J; +////function /*f8Def*/f8(ij: any): any { return ij; } +//// /////*f0*/f0(); /////*f1*/f1(); /////*f2*/f2(); +/////*f3*/f3(); +/////*f4*/f4(); +/////*f5*/f5(); +/////*f6*/f6(); +/////*f7*/f7(); +/////*f8*/f8(); verify.goToType({ f0: "I", f1: "T", f2: "I", + f3: "f3Def", + f4: "I", + f5: ["I", "J"], + f6: ["I", "J"], + f7: [], + f8: "f8Def", }); From 026b5039452cd781a509c27e04975775bf63297f Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 6 Aug 2018 16:51:01 -0700 Subject: [PATCH 3/3] If a function returns 'void' or some other type with no definition, just return the function definition. --- src/services/goToDefinition.ts | 20 ++++++++++++------- .../goToTypeDefinition_returnType.ts | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 0c715fae7b0b3..26c91caddb028 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -135,15 +135,21 @@ namespace ts.GoToDefinition { } const symbol = typeChecker.getSymbolAtLocation(node); - const type = symbol && getTypeForTypeDefinition(symbol, node, typeChecker); - return type && flatMap(type.isUnion() && !(type.flags & TypeFlags.Enum) ? type.types : [type], t => - t.symbol && getDefinitionFromSymbol(typeChecker, t.symbol, node)); + if (!symbol) return undefined; + + const typeAtLocation = typeChecker.getTypeOfSymbolAtLocation(symbol, node); + const returnType = tryGetReturnTypeOfFunction(symbol, typeAtLocation, typeChecker); + const fromReturnType = returnType && definitionFromType(returnType, typeChecker, node); + // If a function returns 'void' or some other type with no definition, just return the function definition. + return fromReturnType && fromReturnType.length !== 0 ? fromReturnType : definitionFromType(typeAtLocation, typeChecker, node); } - function getTypeForTypeDefinition(symbol: Symbol, node: Node, checker: TypeChecker): Type | undefined { - const type = checker.getTypeOfSymbolAtLocation(symbol, node); - if (!type) return undefined; + function definitionFromType(type: Type, checker: TypeChecker, node: Node): DefinitionInfo[] { + return flatMap(type.isUnion() && !(type.flags & TypeFlags.Enum) ? type.types : [type], t => + t.symbol && getDefinitionFromSymbol(checker, t.symbol, node)); + } + function tryGetReturnTypeOfFunction(symbol: Symbol, type: Type, checker: TypeChecker): Type | undefined { // If the type is just a function's inferred type, // go-to-type should go to the return type instead, since go-to-definition takes you to the function anyway. if (type.symbol === symbol || @@ -152,7 +158,7 @@ namespace ts.GoToDefinition { const sigs = type.getCallSignatures(); if (sigs.length === 1) return checker.getReturnTypeOfSignature(first(sigs)); } - return type; + return undefined; } export function getDefinitionAndBoundSpan(program: Program, sourceFile: SourceFile, position: number): DefinitionInfoAndBoundSpan | undefined { diff --git a/tests/cases/fourslash/goToTypeDefinition_returnType.ts b/tests/cases/fourslash/goToTypeDefinition_returnType.ts index 1c404bb0e1132..b48dd16b84b72 100644 --- a/tests/cases/fourslash/goToTypeDefinition_returnType.ts +++ b/tests/cases/fourslash/goToTypeDefinition_returnType.ts @@ -42,6 +42,6 @@ verify.goToType({ f4: "I", f5: ["I", "J"], f6: ["I", "J"], - f7: [], + f7: "f7Def", f8: "f8Def", });