diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 4564a571b1f43..21981c8df5f40 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -37,6 +37,12 @@ namespace ts.codefix { type AddNode = PropertyDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction; + export const enum PreserveOptionalFlags { + Method = 1 << 0, + Property = 1 << 1, + All = Method | Property + } + /** * `addClassElement` will not be called if we can't figure out a representation for `symbol` in `enclosingDeclaration`. * @param body If defined, this will be the body of the member node passed to `addClassElement`. Otherwise, the body will default to a stub. @@ -50,6 +56,7 @@ namespace ts.codefix { importAdder: ImportAdder | undefined, addClassElement: (node: AddNode) => void, body: Block | undefined, + preserveOptional = PreserveOptionalFlags.All, isAmbient = false, ): void { const declarations = symbol.getDeclarations(); @@ -83,7 +90,7 @@ namespace ts.codefix { /*decorators*/ undefined, modifiers, name, - optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined, + optional && (preserveOptional & PreserveOptionalFlags.Property) ? factory.createToken(SyntaxKind.QuestionToken) : undefined, typeNode, /*initializer*/ undefined)); break; @@ -158,14 +165,14 @@ namespace ts.codefix { } else { Debug.assert(declarations.length === signatures.length, "Declarations and signatures should match count"); - addClassElement(createMethodImplementingSignatures(checker, context, enclosingDeclaration, signatures, name, optional, modifiers, quotePreference, body)); + addClassElement(createMethodImplementingSignatures(checker, context, enclosingDeclaration, signatures, name, optional && !!(preserveOptional & PreserveOptionalFlags.Method), modifiers, quotePreference, body)); } } break; } function outputMethod(quotePreference: QuotePreference, signature: Signature, modifiers: NodeArray | undefined, name: PropertyName, body?: Block): void { - const method = createSignatureDeclarationFromSignature(SyntaxKind.MethodDeclaration, context, quotePreference, signature, body, name, modifiers, optional, enclosingDeclaration, importAdder); + const method = createSignatureDeclarationFromSignature(SyntaxKind.MethodDeclaration, context, quotePreference, signature, body, name, modifiers, optional && !!(preserveOptional & PreserveOptionalFlags.Method), enclosingDeclaration, importAdder); if (method) addClassElement(method); } } diff --git a/src/services/completions.ts b/src/services/completions.ts index d8b774a9404ba..7035722b3ec83 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -895,7 +895,7 @@ namespace ts.Completions { node => { let requiredModifiers = ModifierFlags.None; if (isAbstract) { - requiredModifiers |= ModifierFlags.Abstract; + requiredModifiers |= ModifierFlags.Abstract; } if (isClassElement(node) && checker.getMemberOverrideModifierStatus(classLikeDeclaration, node) === MemberOverrideStatus.NeedsOverride) { @@ -919,6 +919,7 @@ namespace ts.Completions { completionNodes.push(node); }, body, + codefix.PreserveOptionalFlags.Property, isAbstract); if (completionNodes.length) { @@ -3888,5 +3889,6 @@ namespace ts.Completions { } return charCode; } + } diff --git a/tests/cases/fourslash/completionsOverridingMethod9.ts b/tests/cases/fourslash/completionsOverridingMethod9.ts new file mode 100644 index 0000000000000..8f9cac56feb8a --- /dev/null +++ b/tests/cases/fourslash/completionsOverridingMethod9.ts @@ -0,0 +1,44 @@ +/// + +// @Filename: a.ts +// @newline: LF + +////interface IFoo { +//// a?: number; +//// b?(x: number): void; +////} +////class Foo implements IFoo { +//// /**/ +////} + +verify.completions({ + marker: "", + isNewIdentifierLocation: true, + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithClassMemberSnippets: true, + }, + includes: [ + { + name: "a", + sortText: completion.SortText.LocationPriority, + replacementSpan: { + fileName: "", + pos: 0, + end: 0, + }, + insertText: "a?: number;\n" + }, + { + name: "b", + sortText: completion.SortText.LocationPriority, + replacementSpan: { + fileName: "", + pos: 0, + end: 0, + }, + insertText: "b(x: number): void {\n}\n" + }, + ], +});