diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 695e71b53c2..ddd5bacea26 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -54,6 +54,7 @@ namespace ts.codefix { const nonPrivateAndNotExistedInHeritageClauseMembers = implementedTypeSymbols.filter(and(symbolPointsToNonPrivateMember, symbol => !maybeHeritageClauseSymbol.has(symbol.escapedName))); const classType = checker.getTypeAtLocation(classDeclaration); + const constructor = find(classDeclaration.members, m => isConstructorDeclaration(m)); if (!classType.getNumberIndexType()) { createMissingIndexSignatureDeclaration(implementedType, IndexKind.Number); @@ -62,12 +63,22 @@ namespace ts.codefix { createMissingIndexSignatureDeclaration(implementedType, IndexKind.String); } - createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, context, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, context, preferences, member => insertInterfaceMemberNode(sourceFile, classDeclaration, member)); function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void { const indexInfoOfKind = checker.getIndexInfoOfType(type, kind); if (indexInfoOfKind) { - changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration, /*flags*/ undefined, getNoopSymbolTrackerWithResolver(context))!); + insertInterfaceMemberNode(sourceFile, classDeclaration, checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration, /*flags*/ undefined, getNoopSymbolTrackerWithResolver(context))!); + } + } + + // Either adds the node at the top of the class, or if there's a constructor right after that + function insertInterfaceMemberNode(sourceFile: SourceFile, cls: ClassLikeDeclaration | InterfaceDeclaration, newElement: ClassElement): void { + if (constructor) { + changeTracker.insertNodeAfter(sourceFile, constructor, newElement); + } + else { + changeTracker.insertNodeAtClassStart(sourceFile, cls, newElement); } } } diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts index 98059017098..6a8a48266ef 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts @@ -1,5 +1,6 @@ /// +//// //// interface I { //// x: {}; //// } @@ -8,6 +9,15 @@ //// |]constructor() { } //// } -verify.rangeAfterCodeFix(` -x: {}; -`); +verify.codeFix({ + description: "Implement interface 'I'", + newFileContent:` +interface I { + x: {}; +} + +class C implements I { + constructor() { } + x: {}; +}`, +}); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts index e667783b995..4b61ea41e40 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts @@ -1,5 +1,6 @@ /// +//// //// interface I { //// x: number; //// y: number; @@ -11,6 +12,20 @@ //// y: number; //// } -verify.rangeAfterCodeFix(` -z: number & { __iBrand: any; }; -`); + +verify.codeFix({ + description: "Implement interface 'I'", + newFileContent:` +interface I { + x: number; + y: number; + z: number & { __iBrand: any }; +} + +class C implements I { + constructor(public x: number) { } + z: number & { __iBrand: any; }; + y: number; +}`, +}); + diff --git a/tests/cases/fourslash/codeFixClassImplementInterface_order.ts b/tests/cases/fourslash/codeFixClassImplementInterface_order.ts new file mode 100644 index 00000000000..7e45eeb6cf6 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterface_order.ts @@ -0,0 +1,28 @@ +/// + +// #34841 + +////interface IFoo { +//// bar(): void; +////} +//// +////class Foo implements IFoo { +//// private x = 1; +//// constructor() { this.x = 2 } +////} + +verify.codeFix({ + description: "Implement interface 'IFoo'", + index: 0, + newFileContent: +`interface IFoo { + bar(): void; +} + +class Foo implements IFoo { + private x = 1; + constructor() { this.x = 2 } + bar(): void { + throw new Error("Method not implemented."); + } +}`});