add support of codefix for Strict Class Initialization#21528
add support of codefix for Strict Class Initialization#215289 commits merged intomicrosoft:masterfrom
Conversation
| @@ -0,0 +1,112 @@ | |||
| /* @internal */ | |||
| namespace ts.codefix { | |||
There was a problem hiding this comment.
you do not need to split them into 3 fixes, one code fix can return multiple actions. for instance see https://github.com/Microsoft/TypeScript/blob/master/src/services/codefixes/fixUnusedIdentifier.ts#L11 we will add two actions possibly, one for _ prefix, and one for deleting the declaration, and the user gets to choose.
|
@Andy-MS can you please review |
ghost
left a comment
There was a problem hiding this comment.
Could also use a test that isn't codeFixAll.
| } | ||
|
|
||
| function addDefiniteAssignmentAssertion(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): void { | ||
| const property = clone(propertyDeclaration); |
There was a problem hiding this comment.
This could use updateProperty (once #21577 is in)
| return { token, propertyDeclaration }; | ||
| } | ||
|
|
||
| function getActionsForAddMissingDefiniteAssignmentAssertion (context: CodeFixContext, token: Identifier, propertyDeclaration: PropertyDeclaration): CodeFixAction[] | undefined { |
There was a problem hiding this comment.
thanks for your review
i have not inline this, because it looks clear 😃
|
|
||
| const { token, propertyDeclaration } = info; | ||
| if (!addToSeen(seenNames, token.text)) { | ||
| return; |
There was a problem hiding this comment.
Nit: No need for early return if there's only one line following it, then you could just put that line inside the if and negate the condition.
| }, | ||
| }); | ||
|
|
||
| interface Info { token: Identifier; propertyDeclaration: PropertyDeclaration; } |
There was a problem hiding this comment.
Nit: Not much point returning token since that's just propertyDeclaration.name.
|
|
||
| interface Info { token: Identifier; propertyDeclaration: PropertyDeclaration; } | ||
| function getInfo (sourceFile: SourceFile, pos: number): Info | undefined { | ||
| const token = getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ false); |
There was a problem hiding this comment.
return isIdentifier(token) ? cast(token.parent, isPropertyDeclaration) : undefined;.
| return createLiteral((<LiteralType>type).value); | ||
| } | ||
| else if (type.getFlags() & TypeFlags.Union) { | ||
| for (const t of (<UnionType>type).types) { |
There was a problem hiding this comment.
return firstDefined((<UnionType>type).types, t => getDefaultValueFromType(checker, t));
| } | ||
| else if (type.getFlags() & TypeFlags.Object) { | ||
| const objectType = <ObjectType>type; | ||
| if (getObjectFlags(objectType) & ObjectFlags.Class) { |
There was a problem hiding this comment.
getObjectFlags will handle any type as input, so no need for if (type.getFlags() & TypeFlags.Object) check first. You don't need objectType since none of the uses of type require a more specific type.
| else if (type.getFlags() & TypeFlags.Object) { | ||
| const objectType = <ObjectType>type; | ||
| if (getObjectFlags(objectType) & ObjectFlags.Class) { | ||
| const classDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(objectType.symbol); |
There was a problem hiding this comment.
I think the return type of getClassLikeDeclarationOfSymbol should be changed to ClassLikeDeclaration | undefined.
| if (getObjectFlags(objectType) & ObjectFlags.Class) { | ||
| const classDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(objectType.symbol); | ||
| const constructorDeclaration = find<ClassElement, ConstructorDeclaration>(classDeclaration.members, (m): m is ConstructorDeclaration => isConstructorDeclaration(m) && !!m.body)!; | ||
| if (constructorDeclaration && |
There was a problem hiding this comment.
You might also check that the class isn't abstract. Also, a class with no explicit constructor declaration has an implicit one with no parameters.
| const classDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(objectType.symbol); | ||
| const constructorDeclaration = find<ClassElement, ConstructorDeclaration>(classDeclaration.members, (m): m is ConstructorDeclaration => isConstructorDeclaration(m) && !!m.body)!; | ||
| if (constructorDeclaration && | ||
| (!constructorDeclaration.parameters || !constructorDeclaration.parameters.length) && |
There was a problem hiding this comment.
parameters should always be defined. Don't think you need to look at typeParameters since those are illegal on a constructor.
57b1033 to
8dd5037
Compare
| "category": "Message", | ||
| "code": 95017 | ||
| }, | ||
| "add Undefined type to property '{0}'": { |
There was a problem hiding this comment.
Nit: Start with a capital letter, but don't capitalize other things (e.g. say "definite assignment assertion" not "Definite Assignment Assertion".) Also put 'undefined' in single quotes.
| if (!propertyDeclaration) return undefined; | ||
|
|
||
| const newLineCharacter = getNewLineOrDefaultFromHost(context.host, context.formatContext.options); | ||
| return [ |
There was a problem hiding this comment.
getActionsForAddMissingInitializer is the only one that can be undefined. Maybe you could use ...optionToArray(getActionsForMissingInitializer(...)) (#21628) instead of filter.
| }, | ||
| fixIds: [fixIdAddDefiniteAssignmentAssertions, fixIdAddUndefinedType, fixIdAddInitializer], | ||
| getAllCodeActions: context => { | ||
| const seenNames = createMap<true>(); |
There was a problem hiding this comment.
Are you sure we need this? I don't think we would have two of these errors for the same property. But we might get this error for two separate properties that happen to share a name, which would result in only one fix here.
| else if (type.flags & TypeFlags.Union) { | ||
| return firstDefined((<UnionType>type).types, t => getDefaultValueFromType(checker, t)); | ||
| } | ||
| else if (type.flags & TypeFlags.Object) { |
There was a problem hiding this comment.
Earlier comment marked as out of date, so copying here: getObjectFlags will handle any type as input, so no need for if (type.getFlags() & TypeFlags.Object) check first.
| //// foo: T; | ||
| //// } | ||
|
|
||
| verify.codeFixAll({ |
There was a problem hiding this comment.
Also add tests using verify.codeFixAvailable instead of verify.codeFixAll.
| "category": "Message", | ||
| "code": 95018 | ||
| }, | ||
| "add default Initializer to property '{0}'": { |
There was a problem hiding this comment.
Nit: I think this can just say "initializer", not "default initializer".
984c2c8 to
4b1e444
Compare
|
ping @Andy-MS |
| return firstDefined((<UnionType>type).types, t => getDefaultValueFromType(checker, t)); | ||
| } | ||
| else { | ||
| if (getObjectFlags(type) & ObjectFlags.Class) { |
| //// foo: T; | ||
| //// } | ||
|
|
||
| verify.codeFixAvailable(); |
There was a problem hiding this comment.
Also test the content of the code fix -- use verify.codeFix and verify.codeFixAll (in separate tests).
| errorCodes, | ||
| getCodeActions: (context) => { | ||
| const propertyDeclaration = getPropertyDeclaration(context.sourceFile, context.span.start); | ||
| if (!propertyDeclaration) return undefined; |
There was a problem hiding this comment.
Nit: Just return; not return undefined; since this is a void function.
| getActionsForAddMissingDefiniteAssignmentAssertion(context, propertyDeclaration, newLineCharacter) | ||
| ]; | ||
| let action: CodeFixAction | undefined; | ||
| if ((action = getActionsForAddMissingInitializer(context, propertyDeclaration, newLineCharacter))) result.push(action); |
There was a problem hiding this comment.
append(result, getActionsForAddMissingInitializer(context, propertyDeclaration, newLineCharacter)))
| return isIdentifier(token) ? cast(token.parent, isPropertyDeclaration) : undefined; | ||
| } | ||
|
|
||
| function getActionsForAddMissingDefiniteAssignmentAssertion (context: CodeFixContext, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): CodeFixAction { |
There was a problem hiding this comment.
Name the function getActionFor not getActionsFor if it doesn't return an array
4b1e444 to
5d1bf55
Compare
… codefix-with-strictPropertyInitialization
|
Could you merge from master again and fix the lint failure in |
|
Thanks! |
Fixes #21509