From bb33cc87ab6bf5d6c1f97b0303f217178babadc8 Mon Sep 17 00:00:00 2001 From: Danyal Ahmed <58849388+danyalahmed1995@users.noreply.github.com> Date: Mon, 11 May 2026 02:32:30 +0500 Subject: [PATCH 1/2] Fix implement-interface codefix for invalid interface types --- .../fixClassIncorrectlyImplementsInterface.ts | 17 +++++++++++++++-- ...plementInterfaceTypeParamInstantiateError.ts | 3 +-- ...ixClassImplementInterfaceUndeclaredSymbol.ts | 3 +-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 49298f9058053..8aefd834ce3fe 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -5,7 +5,6 @@ import { createMissingMemberNodes, getNoopSymbolTrackerWithResolver, registerCodeFix, - TypeConstructionContext, } from "../_namespaces/ts.codefix.js"; import { addToSeen, @@ -13,6 +12,7 @@ import { ClassElement, ClassLikeDeclaration, CodeFixAction, + CodeFixContextBase, createSymbolTable, Debug, Diagnostics, @@ -30,6 +30,7 @@ import { isConstructorDeclaration, mapDefined, ModifierFlags, + Node, SourceFile, Symbol, SymbolTable, @@ -76,7 +77,7 @@ function symbolPointsToNonPrivateMember(symbol: Symbol) { } function addMissingDeclarations( - context: TypeConstructionContext, + context: CodeFixContextBase, implementedTypeNode: ExpressionWithTypeArguments, sourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, @@ -90,6 +91,12 @@ function addMissingDeclarations( const implementedType = checker.getTypeAtLocation(implementedTypeNode) as InterfaceType; const implementedTypeSymbols = checker.getPropertiesOfType(implementedType); const nonPrivateAndNotExistedInHeritageClauseMembers = implementedTypeSymbols.filter(and(symbolPointsToNonPrivateMember, symbol => !maybeHeritageClauseSymbol.has(symbol.escapedName))); + if ( + hasBlockingSemanticError(context, implementedTypeNode) || + nonPrivateAndNotExistedInHeritageClauseMembers.some(symbol => symbol.getDeclarations()?.some(declaration => hasBlockingSemanticError(context, declaration))) + ) { + return; + } const classType = checker.getTypeAtLocation(classDeclaration); const constructor = find(classDeclaration.members, m => isConstructorDeclaration(m)); @@ -123,6 +130,12 @@ function addMissingDeclarations( } } +function hasBlockingSemanticError(context: CodeFixContextBase, node: Node): boolean { + return context.program + .getSemanticDiagnostics(node.getSourceFile(), context.cancellationToken, [node]) + .some(diagnostic => !errorCodes.includes(diagnostic.code)); +} + function getHeritageClauseSymbolTable(classDeclaration: ClassLikeDeclaration, checker: TypeChecker): SymbolTable { const heritageClauseNode = getEffectiveBaseTypeNode(classDeclaration); if (!heritageClauseNode) return createSymbolTable(); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateError.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateError.ts index dc816ae86551b..cdc05c06abf3b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateError.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateError.ts @@ -8,5 +8,4 @@ // TODO: (arozga) Don't know how to instantiate in codeFix // if instantiation is invalid. -// Should be verify.codeFixAvailable([]); -verify.codeFixAvailable([{ description: "Implement interface 'I'" }]); +verify.codeFixAvailable([]); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceUndeclaredSymbol.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceUndeclaredSymbol.ts index c5bb11e1b40b6..94c5f7c70fe57 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceUndeclaredSymbol.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceUndeclaredSymbol.ts @@ -15,5 +15,4 @@ // Since we can't guess the programmer's intent here, we do nothing. // TODO: (aozgaa) Acknowledge other errors on class/implemented interface/extended abstract class. -// Should be verify.codeFixAvailable([]); -verify.codeFixAvailable([{ description: "Implement interface 'I'" }]); +verify.codeFixAvailable([]); From 456f7d951b1f6a869b818b35d24a3c44b818a566 Mon Sep 17 00:00:00 2001 From: Danyal Ahmed <58849388+danyalahmed1995@users.noreply.github.com> Date: Mon, 11 May 2026 02:44:12 +0500 Subject: [PATCH 2/2] Avoid repeated diagnostics in implement-interface fix --- .../fixClassIncorrectlyImplementsInterface.ts | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 8aefd834ce3fe..535a03526367c 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -15,6 +15,7 @@ import { CodeFixContextBase, createSymbolTable, Debug, + Diagnostic, Diagnostics, ExpressionWithTypeArguments, find, @@ -91,9 +92,10 @@ function addMissingDeclarations( const implementedType = checker.getTypeAtLocation(implementedTypeNode) as InterfaceType; const implementedTypeSymbols = checker.getPropertiesOfType(implementedType); const nonPrivateAndNotExistedInHeritageClauseMembers = implementedTypeSymbols.filter(and(symbolPointsToNonPrivateMember, symbol => !maybeHeritageClauseSymbol.has(symbol.escapedName))); + const semanticDiagnostics = new Map(); if ( - hasBlockingSemanticError(context, implementedTypeNode) || - nonPrivateAndNotExistedInHeritageClauseMembers.some(symbol => symbol.getDeclarations()?.some(declaration => hasBlockingSemanticError(context, declaration))) + hasBlockingSemanticError(context, semanticDiagnostics, implementedTypeNode) || + nonPrivateAndNotExistedInHeritageClauseMembers.some(symbol => symbol.getDeclarations()?.some(declaration => hasBlockingSemanticError(context, semanticDiagnostics, declaration))) ) { return; } @@ -130,10 +132,20 @@ function addMissingDeclarations( } } -function hasBlockingSemanticError(context: CodeFixContextBase, node: Node): boolean { - return context.program - .getSemanticDiagnostics(node.getSourceFile(), context.cancellationToken, [node]) - .some(diagnostic => !errorCodes.includes(diagnostic.code)); +function hasBlockingSemanticError(context: CodeFixContextBase, semanticDiagnostics: Map, node: Node): boolean { + const sourceFile = node.getSourceFile(); + let diagnostics = semanticDiagnostics.get(sourceFile); + if (!diagnostics) { + diagnostics = context.program.getSemanticDiagnostics(sourceFile, context.cancellationToken); + semanticDiagnostics.set(sourceFile, diagnostics); + } + return diagnostics.some(diagnostic => + diagnostic.start !== undefined && + diagnostic.length !== undefined && + !errorCodes.includes(diagnostic.code) && + diagnostic.start >= node.pos && + diagnostic.start + diagnostic.length <= node.end + ); } function getHeritageClauseSymbolTable(classDeclaration: ClassLikeDeclaration, checker: TypeChecker): SymbolTable {