diff --git a/docs/rules/no-constructor-return.md b/docs/rules/no-constructor-return.md new file mode 100644 index 00000000000..327ab0cf404 --- /dev/null +++ b/docs/rules/no-constructor-return.md @@ -0,0 +1,49 @@ +# Disallow returning value in constructor (no-constructor-return) + +In JavaScript returning value in constructor is allowed, however this looks meaningless. Forbidding this pattern prevents mistake resulting from unfamiliarity with the language or copy-paste error. + +Note that returning nothing with flow control is allowed. + +## Rule Details + +This rule disallows return statement in constructor of a class. + +Examples of **incorrect** code for this rule: + +```js +/*eslint no-constructor-return: "error"*/ + +class A { + constructor() { + return 'a'; + } +} + +class B { + constructor(f) { + if (!f) { + return 'falsy'; + } + } +} +``` + +Examples of **correct** code for this rule: + +```js +/*eslint no-constructor-return: "error"*/ + +class C { + constructor() { } +} + +class D { + constructor(f) { + if (!f) { + return; // Flow control. + } + + f(); + } +} +``` diff --git a/lib/rules/index.js b/lib/rules/index.js index dbda93fb325..047a1e73f26 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -101,6 +101,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-console": () => require("./no-console"), "no-const-assign": () => require("./no-const-assign"), "no-constant-condition": () => require("./no-constant-condition"), + "no-constructor-return": () => require("./no-constructor-return"), "no-continue": () => require("./no-continue"), "no-control-regex": () => require("./no-control-regex"), "no-debugger": () => require("./no-debugger"), diff --git a/lib/rules/no-constructor-return.js b/lib/rules/no-constructor-return.js new file mode 100644 index 00000000000..ee6ead40b5f --- /dev/null +++ b/lib/rules/no-constructor-return.js @@ -0,0 +1,52 @@ +/** + * @fileoverview Rule to disallow returning value from constructor. + * @author Pig Fang + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "disallow returning value from constructor", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/no-constructor-return" + }, + + schema: {}, + + fixable: "code", + + messages: { + unexpected: "Unexpected return statement in constructor." + } + }, + + create(context) { + return { + "MethodDefinition[kind='constructor'] ReturnStatement"(node) { + const ancestors = context.getAncestors(); + const ancestor = ancestors[ancestors.length - 3]; + + if ( + ancestor && + ancestor.type === "MethodDefinition" && + ancestor.kind === "constructor" || + node.argument + ) { + context.report({ + node, + messageId: "unexpected" + }); + } + } + }; + } +}; diff --git a/tests/lib/rules/no-constructor-return.js b/tests/lib/rules/no-constructor-return.js new file mode 100644 index 00000000000..9e5202ac839 --- /dev/null +++ b/tests/lib/rules/no-constructor-return.js @@ -0,0 +1,46 @@ +/** + * @fileoverview Tests for no-constructor-return rule. + * @author Pig Fang + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/no-constructor-return"), + { RuleTester } = require("../../../lib/rule-tester"); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); + +const errors = [{ type: "ReturnStatement", messageId: "unexpected" }]; + +ruleTester.run("no-constructor-return", rule, { + valid: [ + "class C { }", + "class C { constructor() {} }", + "class C { constructor() { let v } }", + "class C { method() { return '' } }", + "class C { get value() { return '' } }", + "class C { constructor(a) { if (!a) { return } else { a() } } }" + ], + invalid: [ + { + code: "class C { constructor() { return } }", + errors + }, + { + code: "class C { constructor() { return '' } }", + errors + }, + { + code: "class C { constructor(a) { if (!a) { return '' } else { a() } } }", + errors + } + ] +}); diff --git a/tools/rule-types.json b/tools/rule-types.json index 4bc07d4e43e..49b66469ea8 100644 --- a/tools/rule-types.json +++ b/tools/rule-types.json @@ -88,6 +88,7 @@ "no-console": "suggestion", "no-const-assign": "problem", "no-constant-condition": "problem", + "no-constructor-return": "suggestion", "no-continue": "suggestion", "no-control-regex": "problem", "no-debugger": "problem",