abstract class expr instantiation and abstract fix works with class expr
This commit is contained in:
parent
ad011108e6
commit
3cfac081d5
4 changed files with 78 additions and 35 deletions
|
@ -19710,6 +19710,9 @@ namespace ts {
|
|||
// This is a declaration, call getSymbolOfNode
|
||||
return getSymbolOfNode(node.parent);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ClassKeyword && node.parent.kind === SyntaxKind.ClassExpression) {
|
||||
return getSymbolOfNode(node.parent);
|
||||
}
|
||||
else if (isLiteralComputedPropertyDeclarationName(node)) {
|
||||
return getSymbolOfNode(node.parent.parent);
|
||||
}
|
||||
|
|
|
@ -2,46 +2,58 @@
|
|||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2.code],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
const checker = context.program.getTypeChecker();
|
||||
getCodeActions: getActionForClassLikeMissingAbstractMember
|
||||
});
|
||||
|
||||
if (token.kind === SyntaxKind.Identifier && isClassLike(token.parent)) {
|
||||
const classDecl = <ClassDeclaration>token.parent;
|
||||
const startPos = classDecl.members.pos;
|
||||
registerCodeFix({
|
||||
errorCodes: [Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1.code],
|
||||
getCodeActions: getActionForClassLikeMissingAbstractMember
|
||||
});
|
||||
|
||||
const InstantiatedExtendsType = checker.getTypeFromTypeReference(getClassExtendsHeritageClauseElement(classDecl)) as InterfaceType;
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
// so duplicates cannot occur.
|
||||
const extendsSymbols = checker.getPropertiesOfType(InstantiatedExtendsType);
|
||||
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
|
||||
function getActionForClassLikeMissingAbstractMember(context: CodeFixContext): CodeAction[] | undefined {
|
||||
const sourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
// This is the identifier in the case of a class declaration
|
||||
// or the class keyword token in the case of a class expression.
|
||||
const token = getTokenAtPosition(sourceFile, start);
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
|
||||
if (isClassLike(token.parent)) {
|
||||
const classDecl = token.parent as ClassLikeDeclaration;
|
||||
const startPos = classDecl.members.pos;
|
||||
|
||||
if (insertion.length) {
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: insertion
|
||||
}]
|
||||
const classType = checker.getTypeAtLocation(classDecl) as InterfaceType;
|
||||
const instantiatedExtendsType = checker.getBaseTypes(classType)[0];
|
||||
|
||||
// Note that this is ultimately derived from a map indexed by symbol names,
|
||||
// so duplicates cannot occur.
|
||||
const extendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType);
|
||||
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
|
||||
|
||||
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
|
||||
|
||||
if (insertion.length) {
|
||||
return [{
|
||||
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: startPos, length: 0 },
|
||||
newText: insertion
|
||||
}]
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
|
||||
const decls = symbol.getDeclarations();
|
||||
Debug.assert(!!(decls && decls.length > 0));
|
||||
const flags = getModifierFlags(decls[0]);
|
||||
return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract);
|
||||
}]
|
||||
}];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return undefined;
|
||||
|
||||
}
|
||||
|
||||
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
|
||||
const decls = symbol.getDeclarations();
|
||||
Debug.assert(!!(decls && decls.length > 0));
|
||||
const flags = getModifierFlags(decls[0]);
|
||||
return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// function foo<T>(a: T) {
|
||||
//// abstract class C<U> {
|
||||
//// abstract a: T | U;
|
||||
//// }
|
||||
//// return C;
|
||||
//// }
|
||||
////
|
||||
//// let B = class extends foo("s")<number> {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
a: string | number;
|
||||
`);
|
|
@ -0,0 +1,14 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// function foo<T>(a: T) {
|
||||
//// abstract class C<U> {
|
||||
//// abstract a: T | U;
|
||||
//// }
|
||||
//// return C;
|
||||
//// }
|
||||
////
|
||||
//// class B extends foo("s")<number> {[| |]}
|
||||
|
||||
verify.rangeAfterCodeFix(`
|
||||
a: string | number;
|
||||
`);
|
Loading…
Reference in a new issue