diff --git a/src/services/completions.ts b/src/services/completions.ts
index 372819464bc..4160ac80369 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -1117,15 +1117,19 @@ namespace ts.Completions {
}
function addPropertySymbol(symbol: Symbol) {
+ // For a computed property with an accessible name like `Symbol.iterator`,
+ // we'll add a completion for the *name* `Symbol` instead of for the property.
// If this is e.g. [Symbol.iterator], add a completion for `Symbol`.
- const symbolSymbol = firstDefined(symbol.declarations, decl => {
- const name = getNameOfDeclaration(decl);
- const leftName = name && name.kind === SyntaxKind.ComputedPropertyName ? getLeftMostName(name.expression) : undefined;
- return leftName && typeChecker.getSymbolAtLocation(leftName);
- });
- if (symbolSymbol) {
- symbols.push(symbolSymbol);
- symbolToOriginInfoMap[getSymbolId(symbolSymbol)] = { type: "symbol-member" };
+ const computedPropertyName = firstDefined(symbol.declarations, decl => tryCast(getNameOfDeclaration(decl), isComputedPropertyName));
+ if (computedPropertyName) {
+ const leftMostName = getLeftMostName(computedPropertyName.expression); // The completion is for `Symbol`, not `iterator`.
+ const nameSymbol = leftMostName && typeChecker.getSymbolAtLocation(leftMostName);
+ // If this is nested like for `namespace N { export const sym = Symbol(); }`, we'll add the completion for `N`.
+ const firstAccessibleSymbol = nameSymbol && getFirstSymbolInChain(nameSymbol, contextToken, typeChecker);
+ if (firstAccessibleSymbol && !symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)]) {
+ symbols.push(firstAccessibleSymbol);
+ symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] = { type: "symbol-member" };
+ }
}
else {
symbols.push(symbol);
diff --git a/tests/cases/fourslash/completionsUniqueSymbol.ts b/tests/cases/fourslash/completionsUniqueSymbol.ts
new file mode 100644
index 00000000000..a00fe0d7167
--- /dev/null
+++ b/tests/cases/fourslash/completionsUniqueSymbol.ts
@@ -0,0 +1,22 @@
+///
+
+////declare const Symbol: () => symbol;
+////namespace M {
+//// export const sym = Symbol();
+////}
+////namespace N {
+//// const sym = Symbol();
+//// export interface I {
+//// [sym]: number;
+//// [M.sym]: number;
+//// }
+////}
+////
+////declare const i: N.I;
+////i[|./**/|];
+
+verify.completions({
+ marker: "",
+ exact: { name: "M", insertText: "[M]", replacementSpan: test.ranges()[0] },
+ preferences: { includeInsertTextCompletions: true },
+});
diff --git a/tests/cases/fourslash/completionsUniqueSymbol_import.ts b/tests/cases/fourslash/completionsUniqueSymbol_import.ts
new file mode 100644
index 00000000000..f15bc59abff
--- /dev/null
+++ b/tests/cases/fourslash/completionsUniqueSymbol_import.ts
@@ -0,0 +1,26 @@
+///
+
+// @Filename: /a.ts
+////declare const Symbol: () => symbol;
+////const privateSym = Symbol();
+////export const publicSym = Symbol();
+////export interface I {
+//// [privateSym]: number;
+//// [publicSym]: number;
+//// n: number;
+////}
+////export const i: I;
+
+// @Filename: /user.ts
+////import { i } from "./a";
+////i[|./**/|];
+
+verify.completions({
+ marker: "",
+ // TODO: GH#25095 Should include `publicSym`
+ exact: "n",
+ preferences: {
+ includeInsertTextCompletions: true,
+ includeCompletionsForModuleExports: true,
+ },
+});