Skip to content

Commit b1e97b3

Browse files
committed
Get one fix per interface
1 parent d842a6f commit b1e97b3

File tree

3 files changed

+53
-32
lines changed

3 files changed

+53
-32
lines changed

src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ namespace ts.codefix {
99
const checker = context.program.getTypeChecker();
1010

1111
if (token.kind === SyntaxKind.Identifier && isClassLike(token.parent)) {
12-
const classDeclaration = <ClassDeclaration>token.parent;
13-
const startPos = classDeclaration.members.pos;
12+
const classDecl = <ClassDeclaration>token.parent;
13+
const startPos = classDecl.members.pos;
1414

15-
const insertion = getMissingAbstractMemberInsertion(classDeclaration, checker, context.newLineCharacter);
15+
const InstantiatedExtendsType = <InterfaceType>checker.getTypeFromTypeReference(getClassExtendsHeritageClauseElement(classDecl));
16+
const resolvedExtendsType = checker.resolveStructuredTypeMembers(InstantiatedExtendsType);
17+
18+
const insertion = getMissingAbstractMembersInsertion(classDecl, resolvedExtendsType, checker, context.newLineCharacter);
1619

1720
if (insertion.length > 0) {
1821
return [{

src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,57 @@ namespace ts.codefix {
88
const token = getTokenAtPosition(sourceFile, start);
99
const checker = context.program.getTypeChecker();
1010

11-
if (token.kind === SyntaxKind.Identifier && isClassLike(token.parent)) {
12-
const classDeclaration = <ClassDeclaration>token.parent;
13-
const startPos: number = classDeclaration.members.pos;
11+
if (!(token.kind === SyntaxKind.Identifier && isClassLike(token.parent))) {
12+
return undefined;
13+
}
14+
const classDecl = <ClassDeclaration>token.parent;
15+
const startPos: number = classDecl.members.pos;
16+
17+
const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
18+
const implementedTypes = implementedTypeNodes.map(checker.getTypeFromTypeReference);
19+
const resolvedImplementedTypes = implementedTypes.map(checker.resolveStructuredTypeMembers);
20+
21+
let result: CodeAction[] | undefined = undefined;
22+
23+
for (const resolvedType of resolvedImplementedTypes) {
24+
const insertion = getMissingMembersInsertion(classDecl, resolvedType, checker, context.newLineCharacter);
25+
result = pushAction(insertion, getLocaleSpecificMessage(Diagnostics.Implement_interface_on_class), result);
26+
}
27+
28+
// TODO: (arozga) Get this working and figure out how to test it reliably.
29+
/*
30+
// If there are multiple objects, we additionally try to generate a combined fix that simultaneously implements all types.
31+
const intersectionType = checker.getIntersectionType(implementedTypes);
32+
if(intersectionType.flags & TypeFlags.Intersection) {
33+
const resolvedIntersectionType = checker.resolveStructuredTypeMembers(<IntersectionType>intersectionType)
34+
const insertion = getMissingMembersInsertion(classDecl, resolvedIntersectionType, checker, context.newLineCharacter);
35+
result = pushAction(insertion, "stubbed locale message", result)
36+
}
37+
*/
1438

15-
const insertion = getMissingInterfaceMembersInsertion(classDeclaration, checker, context.newLineCharacter);
39+
return result;
1640

41+
function pushAction(insertion: string, description: string, result?: CodeAction[]): CodeAction[] {
1742
if (insertion && insertion.length) {
18-
return [{
19-
description: getLocaleSpecificMessage(Diagnostics.Implement_interface_on_class),
43+
const newAction: CodeAction = {
44+
description: description,
2045
changes: [{
2146
fileName: sourceFile.fileName,
2247
textChanges: [{
2348
span: { start: startPos, length: 0 },
2449
newText: insertion
2550
}]
2651
}]
27-
}];
52+
};
53+
if (result) {
54+
result.push(newAction);
55+
}
56+
else {
57+
result = [newAction];
58+
}
2859
}
60+
return result;
2961
}
30-
return undefined;
3162
}
3263
});
3364
}

src/services/utilities.ts

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,32 +1359,19 @@ namespace ts {
13591359
};
13601360
}
13611361

1362-
export function getMissingAbstractMemberInsertion(classDecl: ClassDeclaration, checker: TypeChecker, newlineChar: string): string {
1362+
export function getMissingAbstractMembersInsertion(classDecl: ClassDeclaration, resolvedType: ResolvedType, checker: TypeChecker, newlineChar: string): string {
13631363
const classSymbol = checker.getSymbolOfNode(classDecl);
1364-
1365-
const InstantiatedExtendsType = <InterfaceType>checker.getTypeFromTypeReference(getClassExtendsHeritageClauseElement(classDecl));
1366-
const resolvedExtendsType = checker.resolveStructuredTypeMembers(InstantiatedExtendsType);
1367-
1368-
const missingMembers = filterMissingMembers(filterAbstract(filterNonPrivate(resolvedExtendsType.members)), classSymbol.members);
1369-
1364+
const missingMembers = filterMissingMembers(filterAbstract(filterNonPrivate(resolvedType.members)), classSymbol.members);
13701365
return getInsertionsForMembers(missingMembers, classDecl, checker, newlineChar);
13711366
}
13721367

1373-
export function getMissingInterfaceMembersInsertion(classDecl: ClassDeclaration, checker: TypeChecker, newlineChar: string): string {
1374-
const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
1375-
1368+
/**
1369+
* Finds members of the resolved type that are missing in the class pointed to by class decl
1370+
* and generates source code for the missing members.
1371+
*/
1372+
export function getMissingMembersInsertion(classDecl: ClassDeclaration, resolvedType: ResolvedType, checker: TypeChecker, newlineChar: string): string {
13761373
const classSymbol = checker.getSymbolOfNode(classDecl);
1377-
1378-
let implementsIntersectionType: IntersectionType | InterfaceType;
1379-
if (implementedTypeNodes.length > 1) {
1380-
implementsIntersectionType = <IntersectionType>checker.getIntersectionType(implementedTypeNodes.map(checker.getTypeFromTypeReference));
1381-
}
1382-
else {
1383-
implementsIntersectionType = <InterfaceType>checker.getTypeFromTypeReference(implementedTypeNodes[0]);
1384-
}
1385-
1386-
const structuredType = checker.resolveStructuredTypeMembers(<IntersectionType | InterfaceType>implementsIntersectionType);
1387-
const missingMembers = filterMissingMembers(filterNonPrivate(structuredType.members), classSymbol.members);
1374+
const missingMembers = filterMissingMembers(filterNonPrivate(resolvedType.members), classSymbol.members);
13881375
return getInsertionsForMembers(missingMembers, classDecl, checker, newlineChar);
13891376
}
13901377

0 commit comments

Comments
 (0)