1515 * limitations under the License.
1616 */
1717
18- import { isConstructorDeclaration , isParameterProperty } from "tsutils" ;
18+ import {
19+ isCallExpression ,
20+ isConstructorDeclaration ,
21+ isExpressionStatement ,
22+ isParameterProperty ,
23+ } from "tsutils" ;
1924import * as ts from "typescript" ;
2025
2126import * as Lint from "../index" ;
2227import { Replacement } from "../language/rule/rule" ;
2328
29+ interface Options {
30+ checkSuperCall : boolean ;
31+ }
32+
33+ const OPTION_CHECK_SUPER_CALL = "check-super-calls" ;
34+
2435export class Rule extends Lint . Rules . AbstractRule {
2536 public static metadata : Lint . IRuleMetadata = {
2637 description : "Prevents blank constructors, as they are redundant." ,
27- optionExamples : [ true ] ,
28- options : null ,
29- optionsDescription : "Not configurable." ,
38+ optionExamples : [ true , [ true , { [ OPTION_CHECK_SUPER_CALL ] : true } ] ] ,
39+ options : {
40+ properties : {
41+ [ OPTION_CHECK_SUPER_CALL ] : { type : "boolean" } ,
42+ } ,
43+ type : "object" ,
44+ } ,
45+ optionsDescription : Lint . Utils . dedent `
46+ An optional object with the property '${ OPTION_CHECK_SUPER_CALL } '.
47+ This is to check for unnecessary constructor parameters for super call` ,
3048 rationale : Lint . Utils . dedent `
3149 JavaScript implicitly adds a blank constructor when there isn't one.
3250 It's not necessary to manually add one in.
@@ -39,12 +57,71 @@ export class Rule extends Lint.Rules.AbstractRule {
3957 public static FAILURE_STRING = "Remove unnecessary empty constructor." ;
4058
4159 public apply ( sourceFile : ts . SourceFile ) : Lint . RuleFailure [ ] {
42- return this . applyWithFunction ( sourceFile , walk ) ;
60+ const options : Options = {
61+ checkSuperCall :
62+ this . ruleArguments . length !== 0 &&
63+ ( this . ruleArguments [ 0 ] as { "check-super-calls" ?: boolean } ) [
64+ "check-super-calls"
65+ ] === true ,
66+ } ;
67+
68+ return this . applyWithFunction ( sourceFile , walk , options ) ;
4369 }
4470}
4571
46- const isEmptyConstructor = ( node : ts . ConstructorDeclaration ) : boolean =>
47- node . body !== undefined && node . body . statements . length === 0 ;
72+ const containsSuper = (
73+ statement : ts . Statement ,
74+ constructorParameters : ts . NodeArray < ts . ParameterDeclaration > ,
75+ ) : boolean => {
76+ if (
77+ isExpressionStatement ( statement ) &&
78+ isCallExpression ( statement . expression ) &&
79+ ts . SyntaxKind . SuperKeyword === statement . expression . expression . kind
80+ ) {
81+ const superArguments = statement . expression . arguments ;
82+
83+ if ( superArguments . length < constructorParameters . length ) {
84+ return true ;
85+ }
86+
87+ if ( superArguments . length === constructorParameters . length ) {
88+ if ( constructorParameters . length === 0 ) {
89+ return true ;
90+ }
91+
92+ for ( const constructorParameter of constructorParameters ) {
93+ for ( const superArgument of superArguments ) {
94+ if ( constructorParameter . name . kind !== superArgument . kind ) {
95+ return false ;
96+ }
97+ }
98+ }
99+
100+ return true ;
101+ }
102+ }
103+
104+ return false ;
105+ } ;
106+
107+ const isEmptyOrContainsOnlySuper = ( node : ts . ConstructorDeclaration , options : Options ) : boolean => {
108+ if ( node . body !== undefined ) {
109+ const { checkSuperCall } = options ;
110+
111+ if ( node . body . statements . length === 0 ) {
112+ return true ;
113+ }
114+
115+ if ( checkSuperCall ) {
116+ return (
117+ node . body . statements . length === 1 &&
118+ containsSuper ( node . body . statements [ 0 ] , node . parameters )
119+ ) ;
120+ }
121+ }
122+
123+ return false ;
124+ } ;
48125
49126const containsConstructorParameter = ( node : ts . ConstructorDeclaration ) : boolean =>
50127 // If this has any parameter properties
@@ -59,11 +136,11 @@ const isAccessRestrictingConstructor = (node: ts.ConstructorDeclaration): boolea
59136const containsDecorator = ( node : ts . ConstructorDeclaration ) : boolean =>
60137 node . parameters . some ( p => p . decorators !== undefined ) ;
61138
62- function walk ( context : Lint . WalkContext ) {
139+ function walk ( context : Lint . WalkContext < Options > ) {
63140 const callback = ( node : ts . Node ) : void => {
64141 if (
65142 isConstructorDeclaration ( node ) &&
66- isEmptyConstructor ( node ) &&
143+ isEmptyOrContainsOnlySuper ( node , context . options ) &&
67144 ! containsConstructorParameter ( node ) &&
68145 ! containsDecorator ( node ) &&
69146 ! isAccessRestrictingConstructor ( node )
0 commit comments