diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 29567db03d5..781f14566c9 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -32,6 +32,10 @@ namespace ts.codefix { return Debug.assertDefined(getContainingClass(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false))); } + function symbolPointsToNonPrivateMember (symbol: Symbol) { + return !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private); + } + function addMissingDeclarations( checker: TypeChecker, implementedTypeNode: ExpressionWithTypeArguments, @@ -40,11 +44,12 @@ namespace ts.codefix { changeTracker: textChanges.ChangeTracker, preferences: UserPreferences, ): void { + const maybeHeritageClauseSymbol = getHeritageClauseSymbolTable(classDeclaration, checker); // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. const implementedType = checker.getTypeAtLocation(implementedTypeNode) as InterfaceType; const implementedTypeSymbols = checker.getPropertiesOfType(implementedType); - const nonPrivateMembers = implementedTypeSymbols.filter(symbol => !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private)); + const nonPrivateAndNotExistedInHeritageClauseMembers = implementedTypeSymbols.filter(and(symbolPointsToNonPrivateMember, symbol => !maybeHeritageClauseSymbol.has(symbol.escapedName))); const classType = checker.getTypeAtLocation(classDeclaration)!; @@ -55,7 +60,7 @@ namespace ts.codefix { createMissingIndexSignatureDeclaration(implementedType, IndexKind.String); } - createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); + createMissingMemberNodes(classDeclaration, nonPrivateAndNotExistedInHeritageClauseMembers, checker, preferences, member => changeTracker.insertNodeAtClassStart(sourceFile, classDeclaration, member)); function createMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind): void { const indexInfoOfKind = checker.getIndexInfoOfType(type, kind); @@ -64,4 +69,12 @@ namespace ts.codefix { } } } + + function getHeritageClauseSymbolTable (classDeclaration: ClassLikeDeclaration, checker: TypeChecker): SymbolTable { + const heritageClauseNode = getClassExtendsHeritageClauseElement(classDeclaration); + if (!heritageClauseNode) return createSymbolTable(); + const heritageClauseType = checker.getTypeAtLocation(heritageClauseNode) as InterfaceType; + const heritageClauseTypeSymbols = checker.getPropertiesOfType(heritageClauseType); + return createSymbolTable(heritageClauseTypeSymbols.filter(symbolPointsToNonPrivateMember)); + } } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index a22f8dfbcc6..5d033a82cb3 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -7,25 +7,14 @@ namespace ts.codefix { * @returns Empty string iff there are no member insertions. */ export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: ReadonlyArray, checker: TypeChecker, preferences: UserPreferences, out: (node: ClassElement) => void): void { - const heritageClauseSymbolTable = getHeritageClauseSymbolTable(classDeclaration, checker); - const classMembers = classDeclaration.symbol.members!; for (const symbol of possiblyMissingSymbols) { - if (!classMembers.has(symbol.escapedName) && !(heritageClauseSymbolTable && heritageClauseSymbolTable.has(symbol.escapedName))) { + if (!classMembers.has(symbol.escapedName)) { addNewNodeForMemberSymbol(symbol, classDeclaration, checker, preferences, out); } } } - function getHeritageClauseSymbolTable (classDeclaration: ClassLikeDeclaration, checker: TypeChecker): SymbolTable | undefined { - const heritageClauseNode = getClassExtendsHeritageClauseElement(classDeclaration); - if (!heritageClauseNode) return undefined; - const heritageClauseType = checker.getTypeAtLocation(heritageClauseNode) as InterfaceType; - const heritageClauseTypeSymbols = checker.getPropertiesOfType(heritageClauseType); - const nonPrivateMembers = heritageClauseTypeSymbols.filter(symbol => !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private)); - return createSymbolTable(nonPrivateMembers); - } - /** * @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`. */