diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 9e4e81e3da9..a916e9cbba6 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -108,6 +108,7 @@ namespace ts {
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
+ getExportsAndPropertiesOfModule,
getAmbientModules,
getJsxElementAttributesType,
getJsxIntrinsicTagNames,
@@ -1527,6 +1528,15 @@ namespace ts {
return symbolsToArray(getExportsOfModule(moduleSymbol));
}
+ function getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[] {
+ const exports = getExportsOfModuleAsArray(moduleSymbol);
+ const exportEquals = resolveExternalModuleSymbol(moduleSymbol);
+ if (exportEquals !== moduleSymbol) {
+ addRange(exports, getPropertiesOfType(getTypeOfSymbol(exportEquals)));
+ }
+ return exports;
+ }
+
function tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined {
const symbolTable = getExportsOfModule(moduleSymbol);
if (symbolTable) {
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 954ad21ba29..0906488ea33 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2370,6 +2370,8 @@ namespace ts {
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
getAliasedSymbol(symbol: Symbol): Symbol;
getExportsOfModule(moduleSymbol: Symbol): Symbol[];
+ /** Unlike `getExportsOfModule`, this includes properties of an `export =` value. */
+ /* @internal */ getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[];
getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
getJsxIntrinsicTagNames(): Symbol[];
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index c41c9af9f4e..f0f2f87175e 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -425,8 +425,7 @@ namespace FourSlash {
}
private raiseError(message: string) {
- message = this.messageAtLastKnownMarker(message);
- throw new Error(message);
+ throw new Error(this.messageAtLastKnownMarker(message));
}
private messageAtLastKnownMarker(message: string) {
@@ -723,6 +722,27 @@ namespace FourSlash {
}
}
+ public verifyCompletionsAt(markerName: string, expected: string[]) {
+ this.goToMarker(markerName);
+
+ const actualCompletions = this.getCompletionListAtCaret();
+ if (!actualCompletions) {
+ this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
+ }
+
+ const actual = actualCompletions.entries;
+
+ if (actual.length !== expected.length) {
+ this.raiseError(`Expected ${expected.length} completions, got ${actual.map(a => a.name)}.`);
+ }
+
+ ts.zipWith(actual, expected, (completion, expectedCompletion, index) => {
+ if (completion.name !== expectedCompletion) {
+ this.raiseError(`Expected completion at index ${index} to be ${expectedCompletion}, got ${completion.name}`);
+ }
+ });
+ }
+
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
const completions = this.getCompletionListAtCaret();
if (completions) {
@@ -3166,6 +3186,10 @@ namespace FourSlashInterface {
super(state);
}
+ public completionsAt(markerName: string, completions: string[]) {
+ this.state.verifyCompletionsAt(markerName, completions);
+ }
+
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
}
diff --git a/src/services/completions.ts b/src/services/completions.ts
index d893e7212ec..bb5922af9ad 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -1318,14 +1318,14 @@ namespace ts.Completions {
isMemberCompletion = true;
isNewIdentifierLocation = false;
- let exports: Symbol[];
- const moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importOrExportDeclaration.moduleSpecifier);
- if (moduleSpecifierSymbol) {
- exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol);
+ const moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(moduleSpecifier);
+ if (!moduleSpecifierSymbol) {
+ symbols = emptyArray;
+ return true;
}
- symbols = exports ? filterNamedImportOrExportCompletionItems(exports, namedImportsOrExports.elements) : emptyArray;
-
+ const exports = typeChecker.getExportsAndPropertiesOfModule(moduleSpecifierSymbol);
+ symbols = filterNamedImportOrExportCompletionItems(exports, namedImportsOrExports.elements);
return true;
}
diff --git a/tests/cases/fourslash/completionListForExportEquals.ts b/tests/cases/fourslash/completionListForExportEquals.ts
new file mode 100644
index 00000000000..28b20177c57
--- /dev/null
+++ b/tests/cases/fourslash/completionListForExportEquals.ts
@@ -0,0 +1,16 @@
+
+///
+
+// @Filename: /node_modules/foo/index.d.ts
+////export = Foo;
+////declare var Foo: Foo.Static;
+////declare namespace Foo {
+//// interface Static {
+//// foo(): void;
+//// }
+////}
+
+// @Filename: /a.ts
+////import { /**/ } from "foo";
+
+verify.completionsAt("", ["Static", "foo"]);
diff --git a/tests/cases/fourslash/completionListForExportEquals2.ts b/tests/cases/fourslash/completionListForExportEquals2.ts
new file mode 100644
index 00000000000..a7b0772d55e
--- /dev/null
+++ b/tests/cases/fourslash/completionListForExportEquals2.ts
@@ -0,0 +1,14 @@
+
+///
+
+// @Filename: /node_modules/foo/index.d.ts
+////export = Foo;
+////interface Foo { bar: number; }
+////declare namespace Foo {
+//// interface Static {}
+////}
+
+// @Filename: /a.ts
+////import { /**/ } from "foo";
+
+verify.completionsAt("", ["Static"]);
diff --git a/tests/cases/fourslash/completionListInImportClause04.ts b/tests/cases/fourslash/completionListInImportClause04.ts
index 672c754e807..fa49b57c8d7 100644
--- a/tests/cases/fourslash/completionListInImportClause04.ts
+++ b/tests/cases/fourslash/completionListInImportClause04.ts
@@ -11,10 +11,7 @@
// @Filename: app.ts
////import {/*1*/} from './foo';
-goTo.marker('1');
-verify.completionListContains('prop1');
-verify.completionListContains('prop2');
-verify.not.completionListContains('Foo');
+verify.completionsAt("1", ["prototype", "prop1", "prop2"]);
verify.numberOfErrorsInCurrentFile(0);
goTo.marker('2');
verify.numberOfErrorsInCurrentFile(0);
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index 4397839fbe8..0de303238f1 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -138,6 +138,7 @@ declare namespace FourSlashInterface {
class verify extends verifyNegatable {
assertHasRanges(ranges: Range[]): void;
caretAtMarker(markerName?: string): void;
+ completionsAt(markerName: string, completions: string[]): void;
indentationIs(numberOfSpaces: number): void;
indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle, baseIndentSize?: number): void;
textAtCaretIs(text: string): void;