Skip to content
Permalink
Browse files

New: Add no-useless-constructor rule (fixes #4785)

  • Loading branch information...
alberto committed Dec 23, 2015
1 parent 1ca6d56 commit 97cdb957327594296456c32abc50628d6af7a5b2
@@ -109,6 +109,7 @@
"no-use-before-define": 0,
"no-useless-call": 0,
"no-useless-concat": 0,
"no-useless-constructor": 0,
"no-void": 0,
"no-var": 0,
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
@@ -224,6 +224,7 @@ These rules are only relevant to ES6 environments.
* [no-dupe-class-members](no-dupe-class-members.md) - disallow duplicate name in class members
* [no-this-before-super](no-this-before-super.md) - disallow use of `this`/`super` before calling `super()` in constructors.
* [no-var](no-var.md) - require `let` or `const` instead of `var`
* [no-useless-constructor](no-useless-constructor.md) - disallow unnecessary constructor
* [object-shorthand](object-shorthand.md) - require method and property shorthand syntax for object literals
* [prefer-arrow-callback](prefer-arrow-callback.md) - suggest using arrow functions as callbacks
* [prefer-const](prefer-const.md) - suggest using `const` declaration for variables that are never modified after declared
@@ -0,0 +1,69 @@
# Disallow unnecessary constructor (no-useless-constructor)

ES2015 provides a default class constructor if one is not specified. As such, it is unnecessary to provide an empty constructor or one that simply delegates into its parent class, as in the following examples:

```js
class A {
constructor () {
}
}
class A extends B {
constructor (value) {
super(value);
}
}
```

## Rule Details

This rule flags class constructors that can be safely removed without changing how the class works.

The following patterns are considered problems:

```js
/*eslint no-useless-constructor: 2*/
/*eslint-env es6*/
class A {
constructor () {
}
}
class A extends B {
constructor (...args) {
super(...args);
}
}
```

The following patterns are not considered problems:

```js
/*eslint no-useless-constructor: 2*/
class A { }
class A {
constructor () {
doSomething();
}
}
class A extends B {
constructor() {
super('foo');
}
}
class A extends B {
constructor() {
super();
doSomething();
}
}
```

## When Not To Use It

If you don't want to be notified about unnecessary constructors, you can safely disable this rule.
@@ -0,0 +1,63 @@
/**
* @fileoverview Rule to flag the use of redundant constructors in classes.
* @author Alberto Rodríguez
* @copyright 2015 Alberto Rodríguez. All rights reserved.
* See LICENSE file in root directory for full license.
*/
"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {

/**
* Checks whether the constructor body is a redundant super call.
* @param {Array} body - constructor body content.
* @param {Array} ctorParams - The params to check against super call.
* @returns {boolean} true if the construtor body is redundant
*/
function isRedundantSuperCall(body, ctorParams) {
if (body.length !== 1 ||
body[0].type !== "ExpressionStatement" ||
body[0].expression.callee.type !== "Super") {
return false;
}


return body[0].expression.arguments.every(function(arg, index) {
return (arg.type === "Identifier" && arg.name === ctorParams[index].name) ||
(
arg.type === "SpreadElement" &&
ctorParams[index].type === "RestElement" &&
arg.argument.name === ctorParams[index].argument.name
);
});
}

/**
* Checks whether a node is a redundant construtor
* @param {ASTNode} node - node to check
* @returns {void}
*/
function checkForConstructor(node) {
if (node.kind !== "constructor") {
return;
}

var body = node.value.body.body;

if (!node.parent.parent.superClass && body.length === 0 ||
node.parent.parent.superClass && isRedundantSuperCall(body, node.value.params)) {
context.report(node, "Useless constructor.");
}
}


return {
"MethodDefinition": checkForConstructor
};
};

module.exports.schema = [];
@@ -0,0 +1,90 @@
/**
* @fileoverview Tests for no-useless-constructor rule.
* @author Alberto Rodriguez
* @copyright 2015 Alberto Rodriguez. All rights reserved.
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var rule = require("../../../lib/rules/no-useless-constructor");
var RuleTester = require("../../../lib/testers/rule-tester");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

var ruleTester = new RuleTester();
var error = {message: "Useless constructor.", type: "MethodDefinition"};
ruleTester.run("no-useless-constructor", rule, {
valid: [
{
code: "class A { }",
parserOptions: { ecmaVersion: 6 }
},
{
code: "class A { constructor(){ doSomething(); } }",
parserOptions: { ecmaVersion: 6 }
},
{
code: "class A { constructor(){ super('foo'); } }",
parserOptions: { ecmaVersion: 6 }
},
{
code: "class A extends B { constructor(){} }",
parserOptions: { ecmaVersion: 6 }
},
{
code: "class A extends B { constructor(){ super('foo'); } }",
parserOptions: { ecmaVersion: 6 }
},
{
code: "class A extends B { constructor(foo, bar){ super(foo, bar, 1); } }",
parserOptions: { ecmaVersion: 6 }
},
{
code: "class A extends B { constructor(){ super(); doSomething(); } }",
parserOptions: { ecmaVersion: 6 }
},
{
code: "class A extends B { constructor(...args){ super(...args); doSomething(); } }",
parserOptions: { ecmaVersion: 6 }
}
],
invalid: [
{
code: "class A { constructor(){} }",
parserOptions: { ecmaVersion: 6 },
errors: [error]
},
{
code: "class A { 'constructor'(){} }",
parserOptions: { ecmaVersion: 6 },
errors: [error]
},
{
code: "class A extends B { constructor(foo){ super(foo); } }",
parserOptions: { ecmaVersion: 6 },
errors: [error]
},
{
code: "class A extends B { constructor(foo, bar){ super(foo, bar); } }",
parserOptions: { ecmaVersion: 6 },
errors: [error]
},
{
code: "class A extends B { constructor(...args){ super(...args); } }",
parserOptions: { ecmaVersion: 6 },
errors: [error]
},
{
code: "class A extends B { constructor(foo, bar){ super(foo); } }",
parserOptions: { ecmaVersion: 6 },
errors: [error]
}

]
});

0 comments on commit 97cdb95

Please sign in to comment.
You can’t perform that action at this time.