diff --git a/src/services/completions.ts b/src/services/completions.ts
index fbb0cf9406436..508eacee7ac37 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -5239,7 +5239,7 @@ function getCompletionData(
* @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags
*/
function filterClassMembersList(baseSymbols: readonly Symbol[], existingMembers: readonly ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] {
- const existingMemberNames = new Set<__String>();
+ const existingMemberNames = new Set(["prototype" as __String]); // always exclude 'prototype' from completions
for (const m of existingMembers) {
// Ignore omitted expressions for missing members
if (
@@ -5274,7 +5274,6 @@ function getCompletionData(
return baseSymbols.filter(propertySymbol =>
!existingMemberNames.has(propertySymbol.escapedName) &&
- !!propertySymbol.declarations &&
!(getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.Private) &&
!(propertySymbol.valueDeclaration && isPrivateIdentifierClassElementDeclaration(propertySymbol.valueDeclaration))
);
diff --git a/tests/cases/fourslash/completionListClassMembersImplementingDynamicallyCreatedType1.ts b/tests/cases/fourslash/completionListClassMembersImplementingDynamicallyCreatedType1.ts
new file mode 100644
index 0000000000000..756ed8bb500cc
--- /dev/null
+++ b/tests/cases/fourslash/completionListClassMembersImplementingDynamicallyCreatedType1.ts
@@ -0,0 +1,34 @@
+///
+
+//// type Base = {
+//// [K in "foo" | "bar"]: K;
+//// };
+////
+//// class Cls implements Base { /*1*/ }
+
+verify.completions({
+ marker: "1",
+ includes: ["foo", "bar"],
+ isNewIdentifierLocation: true,
+});
+
+verify.completions({
+ marker: "1",
+ includes: [
+ {
+ name: "foo",
+ insertText: `foo: "foo";`,
+ filterText: "foo",
+ },
+ {
+ name: "bar",
+ insertText: `bar: "bar";`,
+ filterText: "bar",
+ },
+ ],
+ isNewIdentifierLocation: true,
+ preferences: {
+ includeCompletionsWithClassMemberSnippets: true,
+ includeCompletionsWithInsertText: true,
+ },
+});
diff --git a/tests/cases/fourslash/completionListClassMembersImplementingDynamicallyCreatedType2.ts b/tests/cases/fourslash/completionListClassMembersImplementingDynamicallyCreatedType2.ts
new file mode 100644
index 0000000000000..76608fac56e85
--- /dev/null
+++ b/tests/cases/fourslash/completionListClassMembersImplementingDynamicallyCreatedType2.ts
@@ -0,0 +1,86 @@
+///
+
+// https://github.com/microsoft/TypeScript/issues/62689
+
+//// type EventType = "start" | "foo" | "bar" | "fooBar" | "end";
+////
+//// type EventDataType = T extends "fooBar"
+//// ? { foo: string; bar: number }
+//// : T extends "foo"
+//// ? { foo: string }
+//// : T extends "bar"
+//// ? { bar: number }
+//// : T extends EventType
+//// ? Record
+//// : never;
+////
+//// type ListenerFunction = (
+//// context: any,
+//// data: EventDataType,
+//// ) => void | Promise;
+////
+//// type EvtListenerMethods = {
+//// [K in EventType as `on${Capitalize}`]?: ListenerFunction;
+//// };
+////
+//// class A implements EvtListenerMethods {
+//// onFoo(context: any, data: EventDataType<"foo">) {}
+//// onBar(context: any, data: { abcd: any }) {}
+//// /*1*/
+//// }
+////
+//// interface EvtListener extends EvtListenerMethods {}
+////
+//// class B implements EvtListener {
+//// onFoo(context: any, data: EventDataType<"foo">) {}
+//// onBar(context: any, data: { abcd: any }) {}
+//// /*2*/
+//// }
+////
+//// interface EvtListenerHardcoded {
+//// onStart?: ListenerFunction<"start">;
+//// onFooBar?: ListenerFunction<"fooBar">;
+//// onFoo?: ListenerFunction<"foo">;
+//// onBar?: ListenerFunction<"bar">;
+//// onEnd?: ListenerFunction<"end">;
+//// }
+////
+//// class C implements EvtListenerHardcoded {
+//// onFoo(context: any, data: EventDataType<"foo">) {}
+//// onBar(context: any, data: { abcd: any }) {}
+//// /*3*/
+//// }
+
+verify.completions({
+ marker: ["1", "2", "3"],
+ includes: ["onStart", "onEnd", "onFooBar"],
+ excludes: ["onFoo", "onBar"],
+ isNewIdentifierLocation: true,
+});
+
+verify.completions({
+ marker: ["1", "2", "3"],
+ includes: [
+ {
+ name: "onStart",
+ insertText: `onStart?: ListenerFunction<"start">;`,
+ filterText: "onStart",
+ },
+ {
+ name: "onEnd",
+ insertText: `onEnd?: ListenerFunction<"end">;`,
+ filterText: "onEnd",
+ },
+ {
+ name: "onFooBar",
+ insertText: `onFooBar?: ListenerFunction<"fooBar">;`,
+ filterText: "onFooBar",
+ },
+ ],
+ excludes: ["onFoo", "onBar"],
+ isNewIdentifierLocation: true,
+ preferences: {
+ includeCompletionsWithClassMemberSnippets: true,
+ includeCompletionsWithInsertText: true,
+ },
+});