diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 2cec3adb8f5..e82c579a54f 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -14,19 +14,37 @@ namespace ts.codefix { } const startPos: number = classDecl.members.pos; - + const classType = checker.getTypeAtLocation(classDecl); const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl); - const result: CodeAction[] = []; + const hasNumericIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.Number); + const hasStringIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.String); + for (const implementedTypeNode of implementedTypeNodes) { - const implementedType = checker.getTypeFromTypeReference(implementedTypeNode); + const implementedType = checker.getTypeFromTypeReference(implementedTypeNode) as InterfaceTypeWithDeclaredMembers; // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. const implementedTypeSymbols = checker.getPropertiesOfType(implementedType); const nonPrivateMembers = implementedTypeSymbols.filter(symbolRefersToNonPrivateMember); - const insertion = getMissingMembersInsertion(classDecl, nonPrivateMembers, checker, context.newLineCharacter); + let insertion = ""; + + if (!hasNumericIndexSignature) { + const typeNumericIndexInfo = implementedType.declaredNumberIndexInfo; + if (typeNumericIndexInfo) { + insertion = checker.indexSignatureToString(typeNumericIndexInfo, SyntaxKind.NumberKeyword, classDecl); + } + } + + if (!hasStringIndexSignature) { + const typeStringIndexInfo = implementedType.declaredStringIndexInfo; + if (typeStringIndexInfo) { + insertion += checker.indexSignatureToString(typeStringIndexInfo, SyntaxKind.StringKeyword, classDecl); + } + } + + insertion += getMissingMembersInsertion(classDecl, nonPrivateMembers, checker, context.newLineCharacter); const message = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); if (insertion) { pushAction(result, insertion, message); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index ef0ca1a9a57..4b921c1a81c 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -5,7 +5,7 @@ namespace ts.codefix { * Finds members of the resolved type that are missing in the class pointed to by class decl * and generates source code for the missing members. * @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for. - * @returns undefined iff there is no insertion available. + * @returns Empty string iff there are no member insertions. */ export function getMissingMembersInsertion(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: Symbol[], checker: TypeChecker, newlineChar: string): string { const classMembers = classDeclaration.symbol.members; @@ -16,9 +16,12 @@ namespace ts.codefix { for (const symbol of missingMembers) { insertion = insertion.concat(getInsertionForMemberSymbol(symbol, classDeclaration, checker, newlineChar)); } - return insertion.length > 0 ? insertion : undefined; + return insertion; } + /** + * @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`. + */ function getInsertionForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, newlineChar: string): string { // const name = symbol.getName(); const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration); @@ -28,8 +31,9 @@ namespace ts.codefix { } const declaration = declarations[0] as Declaration; - const name = declaration.name.getText(); + const name = declaration.name ? declaration.name.getText() : undefined; const visibility = getVisibilityPrefix(getModifierFlags(declaration)); + switch (declaration.kind) { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: @@ -77,14 +81,6 @@ namespace ts.codefix { result += `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`; return result; - case SyntaxKind.ComputedPropertyName: - if (hasDynamicName(declaration)) { - return ""; - } - throw new Error("Not implemented, computed property name."); - case SyntaxKind.IndexSignature: - throw new Error("Not implemented."); - default: return ""; } diff --git a/tests/cases/fourslash/codeFixUnImplementedInterfaceComputedPropertyLiterals.ts b/tests/cases/fourslash/codeFixUnImplementedInterfaceComputedPropertyLiterals.ts index 98f9c978c3d..465f9a5b4f1 100644 --- a/tests/cases/fourslash/codeFixUnImplementedInterfaceComputedPropertyLiterals.ts +++ b/tests/cases/fourslash/codeFixUnImplementedInterfaceComputedPropertyLiterals.ts @@ -6,7 +6,7 @@ //// [1](): string; //// [2]: boolean; //// } - +//// //// class C implements I {[| |]} verify.rangeAfterCodeFix(` diff --git a/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesBoth.ts b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesBoth.ts new file mode 100644 index 00000000000..e47eeb6d5c1 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesBoth.ts @@ -0,0 +1,14 @@ +/// + + +//// interface I { +//// [x: number]: I; +//// [y: string]: I; +//// } +//// +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + [x: number]: I; + [y: string]: I; +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesNoFix.ts b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesNoFix.ts new file mode 100644 index 00000000000..07896b4cdb3 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesNoFix.ts @@ -0,0 +1,11 @@ +/// + +//// interface I4 { +//// [x: string, y: number]: number; +//// } +//// +//// class C implements I {[| |]} + +verify.not.codeFixAvailable(); + + diff --git a/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesNumber.ts b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesNumber.ts new file mode 100644 index 00000000000..0f143442530 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesNumber.ts @@ -0,0 +1,12 @@ +/// + +//// interface I { +//// [x: number]: I; +//// } +//// +//// class C implements I {[| +//// |]} + +verify.rangeAfterCodeFix(` + [x: number]: I; +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesString.ts b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesString.ts new file mode 100644 index 00000000000..37abd751382 --- /dev/null +++ b/tests/cases/fourslash/codeFixUnImplementedInterfaceIndexSignaturesString.ts @@ -0,0 +1,11 @@ +/// + +//// interface I { +//// [x: string]: number; +//// } +//// +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + [x: string]: number; +`); \ No newline at end of file