diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index c8f20746a074f..1f5807c12495c 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -1570,6 +1570,28 @@ module FourSlash {
this.currentCaretPosition = definition.textSpan.start;
}
+ public goToTypeDefinition(definitionIndex: number) {
+ if (definitionIndex === 0) {
+ this.scenarioActions.push('');
+ }
+ else {
+ this.taoInvalidReason = 'GoToTypeDefinition not supported for non-zero definition indices';
+ }
+
+ var definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
+ if (!definitions || !definitions.length) {
+ this.raiseError('goToTypeDefinition failed - expected to at least one definition location but got 0');
+ }
+
+ if (definitionIndex >= definitions.length) {
+ this.raiseError('goToTypeDefinition failed - definitionIndex value (' + definitionIndex + ') exceeds definition list size (' + definitions.length + ')');
+ }
+
+ var definition = definitions[definitionIndex];
+ this.openFile(definition.fileName);
+ this.currentCaretPosition = definition.textSpan.start;
+ }
+
public verifyDefinitionLocationExists(negative: boolean) {
this.taoInvalidReason = 'verifyDefinitionLocationExists NYI';
@@ -1589,8 +1611,18 @@ module FourSlash {
var assertFn = negative ? assert.notEqual : assert.equal;
var definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
+ var actualCount = definitions && definitions.length || 0;
+
+ assertFn(actualCount, expectedCount, this.messageAtLastKnownMarker("Definitions Count"));
+ }
+
+ public verifyTypeDefinitionsCount(negative: boolean, expectedCount: number) {
+ var assertFn = negative ? assert.notEqual : assert.equal;
+
+ var definitions = this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
+ var actualCount = definitions && definitions.length || 0;
- assertFn(definitions.length, expectedCount, this.messageAtLastKnownMarker("Definitions Count"));
+ assertFn(actualCount, expectedCount, this.messageAtLastKnownMarker("Type definitions Count"));
}
public verifyDefinitionsName(negative: boolean, expectedName: string, expectedContainerName: string) {
diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index f4f0c9d83912d..0e0b8d829183d 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -342,6 +342,9 @@ module Harness.LanguageService {
getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position));
}
+ getTypeDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[]{
+ return unwrapJSONCallResult(this.shim.getTypeDefinitionAtPosition(fileName, position));
+ }
getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position));
}
diff --git a/src/server/client.ts b/src/server/client.ts
index eab42abd0e191..3582d508322c2 100644
--- a/src/server/client.ts
+++ b/src/server/client.ts
@@ -300,6 +300,32 @@ module ts.server {
});
}
+ getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
+ var lineOffset = this.positionToOneBasedLineOffset(fileName, position);
+ var args: protocol.FileLocationRequestArgs = {
+ file: fileName,
+ line: lineOffset.line,
+ offset: lineOffset.offset,
+ };
+
+ var request = this.processRequest(CommandNames.TypeDefinition, args);
+ var response = this.processResponse(request);
+
+ return response.body.map(entry => {
+ var fileName = entry.file;
+ var start = this.lineOffsetToPosition(fileName, entry.start);
+ var end = this.lineOffsetToPosition(fileName, entry.end);
+ return {
+ containerKind: "",
+ containerName: "",
+ fileName: fileName,
+ textSpan: ts.createTextSpanFromBounds(start, end),
+ kind: "",
+ name: ""
+ };
+ });
+ }
+
findReferences(fileName: string, position: number): ReferencedSymbol[]{
// Not yet implemented.
return [];
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index fc9f13a05330a..dbf3ff3e51c9f 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -773,7 +773,7 @@ module ts.server {
findConfigFile(searchPath: string): string {
while (true) {
var fileName = ts.combinePaths(searchPath, "tsconfig.json");
- if (sys.fileExists(fileName)) {
+ if (this.host.fileExists(fileName)) {
return fileName;
}
var parentPath = ts.getDirectoryPath(searchPath);
@@ -923,7 +923,7 @@ module ts.server {
var proj = this.createProject(configFilename, projectOptions);
for (var i = 0, len = parsedCommandLine.fileNames.length; i < len; i++) {
var rootFilename = parsedCommandLine.fileNames[i];
- if (ts.sys.fileExists(rootFilename)) {
+ if (this.host.fileExists(rootFilename)) {
var info = this.openFile(rootFilename, clientFileName == rootFilename);
proj.addRoot(info);
}
diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts
index 5bc1ffbe4dc07..b95d5e99864bb 100644
--- a/src/server/protocol.d.ts
+++ b/src/server/protocol.d.ts
@@ -125,6 +125,14 @@ declare module ts.server.protocol {
export interface DefinitionRequest extends FileLocationRequest {
}
+ /**
+ * Go to type request; value of command field is
+ * "typeDefinition". Return response giving the file locations that
+ * define the type for the symbol found in file at location line, col.
+ */
+ export interface TypeDefinitionRequest extends FileLocationRequest {
+ }
+
/**
* Location in source code expressed as (one-based) line and character offset.
*/
@@ -165,6 +173,13 @@ declare module ts.server.protocol {
body?: FileSpan[];
}
+ /**
+ * Definition response message. Gives text range for definition.
+ */
+ export interface TypeDefinitionResponse extends Response {
+ body?: FileSpan[];
+ }
+
/**
* Get occurrences request; value of command field is
* "occurrences". Return response giving spans that are relevant
diff --git a/src/server/session.ts b/src/server/session.ts
index 18a47201366a7..baf0a085ad4a9 100644
--- a/src/server/session.ts
+++ b/src/server/session.ts
@@ -97,6 +97,7 @@ module ts.server {
export var Rename = "rename";
export var Saveto = "saveto";
export var SignatureHelp = "signatureHelp";
+ export var TypeDefinition = "typeDefinition";
export var Unknown = "unknown";
}
@@ -285,7 +286,29 @@ module ts.server {
}));
}
- getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[] {
+ getTypeDefinition(line: number, offset: number, fileName: string): protocol.FileSpan[] {
+ var file = ts.normalizePath(fileName);
+ var project = this.projectService.getProjectForFile(file);
+ if (!project) {
+ throw Errors.NoProject;
+ }
+
+ var compilerService = project.compilerService;
+ var position = compilerService.host.lineOffsetToPosition(file, line, offset);
+
+ var definitions = compilerService.languageService.getTypeDefinitionAtPosition(file, position);
+ if (!definitions) {
+ return undefined;
+ }
+
+ return definitions.map(def => ({
+ file: def.fileName,
+ start: compilerService.host.positionToLineOffset(def.fileName, def.textSpan.start),
+ end: compilerService.host.positionToLineOffset(def.fileName, ts.textSpanEnd(def.textSpan))
+ }));
+ }
+
+ getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[]{
fileName = ts.normalizePath(fileName);
let project = this.projectService.getProjectForFile(fileName);
@@ -817,6 +840,11 @@ module ts.server {
response = this.getDefinition(defArgs.line, defArgs.offset, defArgs.file);
break;
}
+ case CommandNames.TypeDefinition: {
+ var defArgs = request.arguments;
+ response = this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file);
+ break;
+ }
case CommandNames.References: {
var refArgs = request.arguments;
response = this.getReferences(refArgs.line, refArgs.offset, refArgs.file);
diff --git a/src/services/services.ts b/src/services/services.ts
index d51a9233d452c..1333f2a13cb73 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -1001,6 +1001,8 @@ module ts {
findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[];
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
+ getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
+
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
findReferences(fileName: string, position: number): ReferencedSymbol[];
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[];
@@ -3502,19 +3504,6 @@ module ts {
return ScriptElementKind.unknown;
}
- function getTypeKind(type: Type): string {
- let flags = type.getFlags();
-
- if (flags & TypeFlags.Enum) return ScriptElementKind.enumElement;
- if (flags & TypeFlags.Class) return ScriptElementKind.classElement;
- if (flags & TypeFlags.Interface) return ScriptElementKind.interfaceElement;
- if (flags & TypeFlags.TypeParameter) return ScriptElementKind.typeParameterElement;
- if (flags & TypeFlags.Intrinsic) return ScriptElementKind.primitiveType;
- if (flags & TypeFlags.StringLiteral) return ScriptElementKind.primitiveType;
-
- return ScriptElementKind.unknown;
- }
-
function getSymbolModifiers(symbol: Symbol): string {
return symbol && symbol.declarations && symbol.declarations.length > 0
? getNodeModifiers(symbol.declarations[0])
@@ -3929,6 +3918,71 @@ module ts {
};
}
+ function getDefinitionFromSymbol(symbol: Symbol, node: Node): DefinitionInfo[] {
+ let typeChecker = program.getTypeChecker();
+ let result: DefinitionInfo[] = [];
+ let declarations = symbol.getDeclarations();
+ let symbolName = typeChecker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol
+ let symbolKind = getSymbolKind(symbol, node);
+ let containerSymbol = symbol.parent;
+ let containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : "";
+
+ if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
+ !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
+ // Just add all the declarations.
+ forEach(declarations, declaration => {
+ result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName));
+ });
+ }
+
+ return result;
+
+ function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
+ // Applicable only if we are in a new expression, or we are on a constructor declaration
+ // and in either case the symbol has a construct signature definition, i.e. class
+ if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) {
+ if (symbol.flags & SymbolFlags.Class) {
+ let classDeclaration = symbol.getDeclarations()[0];
+ Debug.assert(classDeclaration && classDeclaration.kind === SyntaxKind.ClassDeclaration);
+
+ return tryAddSignature(classDeclaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result);
+ }
+ }
+ return false;
+ }
+
+ function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
+ if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) {
+ return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result);
+ }
+ return false;
+ }
+
+ function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
+ let declarations: Declaration[] = [];
+ let definition: Declaration;
+
+ forEach(signatureDeclarations, d => {
+ if ((selectConstructors && d.kind === SyntaxKind.Constructor) ||
+ (!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) {
+ declarations.push(d);
+ if ((d).body) definition = d;
+ }
+ });
+
+ if (definition) {
+ result.push(createDefinitionInfo(definition, symbolKind, symbolName, containerName));
+ return true;
+ }
+ else if (declarations.length) {
+ result.push(createDefinitionInfo(lastOrUndefined(declarations), symbolKind, symbolName, containerName));
+ return true;
+ }
+
+ return false;
+ }
+ }
+
/// Goto definition
function getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
synchronizeHostData();
@@ -4003,67 +4057,47 @@ module ts {
declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName));
}
- let result: DefinitionInfo[] = [];
- let declarations = symbol.getDeclarations();
- let symbolName = typeChecker.symbolToString(symbol); // Do not get scoped name, just the name of the symbol
- let symbolKind = getSymbolKind(symbol, node);
- let containerSymbol = symbol.parent;
- let containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : "";
-
- if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
- !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
- // Just add all the declarations.
- forEach(declarations, declaration => {
- result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName));
- });
- }
+ return getDefinitionFromSymbol(symbol, node);
+ }
- return result;
+ /// Goto type
+ function getTypeDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] {
+ synchronizeHostData();
- function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
- // Applicable only if we are in a new expression, or we are on a constructor declaration
- // and in either case the symbol has a construct signature definition, i.e. class
- if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) {
- if (symbol.flags & SymbolFlags.Class) {
- let classDeclaration = symbol.getDeclarations()[0];
- Debug.assert(classDeclaration && classDeclaration.kind === SyntaxKind.ClassDeclaration);
+ let sourceFile = getValidSourceFile(fileName);
- return tryAddSignature(classDeclaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result);
- }
- }
- return false;
+ let node = getTouchingPropertyName(sourceFile, position);
+ if (!node) {
+ return undefined;
}
- function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
- if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) {
- return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result);
- }
- return false;
+ let typeChecker = program.getTypeChecker();
+
+ let symbol = typeChecker.getSymbolAtLocation(node);
+ if (!symbol) {
+ return undefined;
}
- function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
- let declarations: Declaration[] = [];
- let definition: Declaration;
+ let type = typeChecker.getTypeOfSymbolAtLocation(symbol, node);
+ if (!type) {
+ return undefined;
+ }
- forEach(signatureDeclarations, d => {
- if ((selectConstructors && d.kind === SyntaxKind.Constructor) ||
- (!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) {
- declarations.push(d);
- if ((d).body) definition = d;
+ if (type.flags & TypeFlags.Union) {
+ var result: DefinitionInfo[] = [];
+ forEach((type).types, t => {
+ if (t.symbol) {
+ result.push(...getDefinitionFromSymbol(t.symbol, node));
}
});
+ return result;
+ }
- if (definition) {
- result.push(createDefinitionInfo(definition, symbolKind, symbolName, containerName));
- return true;
- }
- else if (declarations.length) {
- result.push(createDefinitionInfo(lastOrUndefined(declarations), symbolKind, symbolName, containerName));
- return true;
- }
-
- return false;
+ if (!type.symbol) {
+ return undefined;
}
+
+ return getDefinitionFromSymbol(type.symbol, node);
}
function getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
@@ -6496,6 +6530,7 @@ module ts {
getSignatureHelpItems,
getQuickInfoAtPosition,
getDefinitionAtPosition,
+ getTypeDefinitionAtPosition,
getReferencesAtPosition,
findReferences,
getOccurrencesAtPosition,
diff --git a/src/services/shims.ts b/src/services/shims.ts
index b5217852b8569..906b8b59d3cb4 100644
--- a/src/services/shims.ts
+++ b/src/services/shims.ts
@@ -132,6 +132,14 @@ module ts {
*/
getDefinitionAtPosition(fileName: string, position: number): string;
+ /**
+ * Returns a JSON-encoded value of the type:
+ * { fileName: string; textSpan: { start: number; length: number}; kind: string; name: string; containerKind: string; containerName: string }
+ *
+ * Or undefined value if no definition can be found.
+ */
+ getTypeDefinitionAtPosition(fileName: string, position: number): string;
+
/**
* Returns a JSON-encoded value of the type:
* { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean }[]
@@ -589,6 +597,20 @@ module ts {
});
}
+ /// GOTO Type
+
+ /**
+ * Computes the definition location of the type of the symbol
+ * at the requested position.
+ */
+ public getTypeDefinitionAtPosition(fileName: string, position: number): string {
+ return this.forwardJSONCall(
+ "getTypeDefinitionAtPosition('" + fileName + "', " + position + ")",
+ () => {
+ return this.languageService.getTypeDefinitionAtPosition(fileName, position);
+ });
+ }
+
public getRenameInfo(fileName: string, position: number): string {
return this.forwardJSONCall(
"getRenameInfo('" + fileName + "', " + position + ")",
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index 867f7b1664148..9c6fd814d9f09 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -119,6 +119,10 @@ module FourSlashInterface {
FourSlash.currentTestState.goToDefinition(definitionIndex);
}
+ public type(definitionIndex: number = 0) {
+ FourSlash.currentTestState.goToTypeDefinition(definitionIndex);
+ }
+
public position(position: number, fileIndex?: number);
public position(position: number, fileName?: string);
public position(position: number, fileNameOrIndex?: any) {
@@ -234,6 +238,10 @@ module FourSlashInterface {
FourSlash.currentTestState.verifyDefinitionsCount(this.negative, expectedCount);
}
+ public typeDefinitionCountIs(expectedCount: number) {
+ FourSlash.currentTestState.verifyTypeDefinitionsCount(this.negative, expectedCount);
+ }
+
public definitionLocationExists() {
FourSlash.currentTestState.verifyDefinitionLocationExists(this.negative);
}
diff --git a/tests/cases/fourslash/goToTypeDefinition.ts b/tests/cases/fourslash/goToTypeDefinition.ts
new file mode 100644
index 0000000000000..6b34e7e48866c
--- /dev/null
+++ b/tests/cases/fourslash/goToTypeDefinition.ts
@@ -0,0 +1,14 @@
+///
+
+// @Filename: goToTypeDefinition_Definition.ts
+/////*definition*/class C {
+//// p;
+////}
+////var c: C;
+
+// @Filename: goToTypeDefinition_Consumption.ts
+/////*reference*/c = undefined;
+
+goTo.marker('reference');
+goTo.type();
+verify.caretAtMarker('definition');
diff --git a/tests/cases/fourslash/goToTypeDefinition2.ts b/tests/cases/fourslash/goToTypeDefinition2.ts
new file mode 100644
index 0000000000000..8c76655f4a832
--- /dev/null
+++ b/tests/cases/fourslash/goToTypeDefinition2.ts
@@ -0,0 +1,18 @@
+///
+
+// @Filename: goToTypeDefinition2_Definition.ts
+/////*definition*/interface I1 {
+//// p;
+////}
+////type propertyType = I1;
+////interface I2 {
+//// property: propertyType;
+////}
+
+// @Filename: goToTypeDefinition2_Consumption.ts
+////var i2: I2;
+////i2.prop/*reference*/erty;
+
+goTo.marker('reference');
+goTo.type();
+verify.caretAtMarker('definition');
diff --git a/tests/cases/fourslash/goToTypeDefinitionAliases.ts b/tests/cases/fourslash/goToTypeDefinitionAliases.ts
new file mode 100644
index 0000000000000..19abc22ca30ef
--- /dev/null
+++ b/tests/cases/fourslash/goToTypeDefinitionAliases.ts
@@ -0,0 +1,24 @@
+///
+
+// @Filename: goToTypeDefinitioAliases_module1.ts
+/////*definition*/interface I {
+//// p;
+////}
+////export {I as I2};
+
+// @Filename: goToTypeDefinitioAliases_module2.ts
+////import {I2 as I3} from "goToTypeDefinitioAliases_module1";
+////var v1: I3;
+////export {v1 as v2};
+
+// @Filename: goToTypeDefinitioAliases_module3.ts
+////import {/*reference1*/v2 as v3} from "goToTypeDefinitioAliases_module2";
+/////*reference2*/v3;
+
+goTo.marker('reference1');
+goTo.type();
+verify.caretAtMarker('definition');
+
+goTo.marker('reference2');
+goTo.type();
+verify.caretAtMarker('definition');
diff --git a/tests/cases/fourslash/goToTypeDefinitionEnumMembers.ts b/tests/cases/fourslash/goToTypeDefinitionEnumMembers.ts
new file mode 100644
index 0000000000000..4b5f151498cb3
--- /dev/null
+++ b/tests/cases/fourslash/goToTypeDefinitionEnumMembers.ts
@@ -0,0 +1,13 @@
+///
+
+/////*definition*/enum E {
+//// value1,
+//// value2
+////}
+////var x = E.value2;
+////
+/////*reference*/x;
+
+goTo.marker('reference');
+goTo.type();
+verify.caretAtMarker('definition');
diff --git a/tests/cases/fourslash/goToTypeDefinitionModule.ts b/tests/cases/fourslash/goToTypeDefinitionModule.ts
new file mode 100644
index 0000000000000..2afccab97c304
--- /dev/null
+++ b/tests/cases/fourslash/goToTypeDefinitionModule.ts
@@ -0,0 +1,19 @@
+///
+
+// @Filename: goToTypeDefinitioAliases_module1.ts
+/////*definition*/module M {
+//// export var p;
+////}
+////var m: typeof M;
+
+// @Filename: goToTypeDefinitioAliases_module3.ts
+/////*reference1*/M;
+/////*reference2*/m;
+
+goTo.marker('reference1');
+goTo.type();
+verify.caretAtMarker('definition');
+
+goTo.marker('reference2');
+goTo.type();
+verify.caretAtMarker('definition');
\ No newline at end of file
diff --git a/tests/cases/fourslash/goToTypeDefinitionPrimitives.ts b/tests/cases/fourslash/goToTypeDefinitionPrimitives.ts
new file mode 100644
index 0000000000000..d38a0c1634390
--- /dev/null
+++ b/tests/cases/fourslash/goToTypeDefinitionPrimitives.ts
@@ -0,0 +1,25 @@
+///
+
+// @Filename: module1.ts
+////var w: {a: number};
+////var x = "string";
+////var y: number | string;
+////var z; // any
+
+// @Filename: module2.ts
+////w./*reference1*/a;
+/////*reference2*/x;
+/////*reference3*/y;
+/////*reference4*/y;
+
+goTo.marker('reference1');
+verify.typeDefinitionCountIs(0);
+
+goTo.marker('reference1');
+verify.typeDefinitionCountIs(0);
+
+goTo.marker('reference2');
+verify.typeDefinitionCountIs(0);
+
+goTo.marker('reference4');
+verify.typeDefinitionCountIs(0);
diff --git a/tests/cases/fourslash/goToTypeDefinitionUnionType.ts b/tests/cases/fourslash/goToTypeDefinitionUnionType.ts
new file mode 100644
index 0000000000000..0630ae3ebb932
--- /dev/null
+++ b/tests/cases/fourslash/goToTypeDefinitionUnionType.ts
@@ -0,0 +1,31 @@
+///
+
+/////*definition0*/class C {
+//// p;
+////}
+////
+/////*definition1*/interface I {
+//// x;
+////}
+////
+////module M {
+//// /*definition2*/export interface I {
+//// y;
+//// }
+////}
+////
+////var x: C | I | M.I;
+////
+/////*reference*/x;
+
+goTo.marker('reference');
+goTo.type(0);
+verify.caretAtMarker('definition0');
+
+goTo.marker('reference');
+goTo.type(1);
+verify.caretAtMarker('definition1');
+
+goTo.marker('reference');
+goTo.type(2);
+verify.caretAtMarker('definition2');
diff --git a/tests/cases/fourslash/server/typedefinition01.ts b/tests/cases/fourslash/server/typedefinition01.ts
new file mode 100644
index 0000000000000..e7c37747abaad
--- /dev/null
+++ b/tests/cases/fourslash/server/typedefinition01.ts
@@ -0,0 +1,12 @@
+///
+
+// @Filename: b.ts
+////import n = require('a');
+////var x/*1*/ = new n.Foo();
+
+// @Filename: a.ts
+//// /*2*/export class Foo {}
+
+goTo.marker('1');
+goTo.type();
+verify.caretAtMarker('2');
\ No newline at end of file
diff --git a/tests/cases/fourslash/shims/goToTypeDefinition.ts b/tests/cases/fourslash/shims/goToTypeDefinition.ts
new file mode 100644
index 0000000000000..6b34e7e48866c
--- /dev/null
+++ b/tests/cases/fourslash/shims/goToTypeDefinition.ts
@@ -0,0 +1,14 @@
+///
+
+// @Filename: goToTypeDefinition_Definition.ts
+/////*definition*/class C {
+//// p;
+////}
+////var c: C;
+
+// @Filename: goToTypeDefinition_Consumption.ts
+/////*reference*/c = undefined;
+
+goTo.marker('reference');
+goTo.type();
+verify.caretAtMarker('definition');