From 6ff5f7d1506bd3908f722657bcdc3070a1087eec Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Sun, 24 Oct 2021 11:54:11 +0530 Subject: [PATCH 1/3] New: Add prefer-object-has-own rule. Fixes #14939 --- docs/rules/prefer-object-has-own.md | 23 +++++++++++++ lib/rules/prefer-object-has-own.js | 43 ++++++++++++++++++++++++ tests/lib/rules/prefer-object-has-own.js | 43 ++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 docs/rules/prefer-object-has-own.md create mode 100644 lib/rules/prefer-object-has-own.js create mode 100644 tests/lib/rules/prefer-object-has-own.js diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md new file mode 100644 index 00000000000..f8a6f3dd957 --- /dev/null +++ b/docs/rules/prefer-object-has-own.md @@ -0,0 +1,23 @@ +# Prefer use of Object.hasOwn over `Object.prototype.hasOwnPrototype` (prefer-object-has-own) + +When Object.prototype.hasOwnPrototype.call is used, this rule requires using the `Object.hasOwn` instead. `Object.hasOwn` is a syntactic sugar and makes the code cleaner. + +## Rule Details + +Examples of **incorrect** code for this rule: + +```js +/*eslint prefer-object-has-own: "error"*/ +Object.prototype.hasOwnProperty.call(obj, "a"); + +let a = Object.prototype.hasOwnProperty; +a.call(obj, "a"); +``` + +Examples of **correct** code for this rule: + +```js +/*eslint prefer-object-has-own: "error"*/ + +Object.hasOwn(obj, "a"); +``` diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js new file mode 100644 index 00000000000..437c64603e0 --- /dev/null +++ b/lib/rules/prefer-object-has-own.js @@ -0,0 +1,43 @@ +/** + * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty + * @author Gautam Arora + * See LICENSE file in root directory for full license. + */ + +"use strict"; + +module.exports = { + meta: { + type: "suggestion", + docs: { + description: + "disallow use of Object.prototype.hasOwnProperty and prefer use of Object.hasOwn", + recommended: "false", + url: "https://eslint.org/docs/rules/prefer-object-has-own" + }, + schema: [], + messages: { + useHasOwnMessage: + "Use Object.hasOwn instead of Object.prototype.hasOwnProperty." + } + }, + create(context) { + + // declare the state of the rule + return { + MemberExpression(node) { + if ( + node.property.name === "hasOwnProperty" && + node.object.object.name === "Object" + ) { + const messageId = "useHasOwnMessage"; + + context.report({ + messageId, + node + }); + } + } + }; + } +}; diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js new file mode 100644 index 00000000000..8b18e1c200a --- /dev/null +++ b/tests/lib/rules/prefer-object-has-own.js @@ -0,0 +1,43 @@ +/** + * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty + * @author Gautam Arora + * See LICENSE file in root directory for full license. + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require("../../../lib/rules/prefer-object-has-own"); +const { RuleTester } = require("../../../lib/rule-tester"); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const parserOptions = { + ecmaVersion: 2018, + sourceType: "module" +}; + +const ruleTester = new RuleTester({ parserOptions }); + +ruleTester.run("prefer-object-has-own", rule, { + valid: [ + ` + let obj = {}; + Object.hasOwn(obj,""); + ` + ], + invalid: [ + ` + let a = Object.prototype.hasOwnProperty(); + obj.call(); + `, + ` + let a = Object.prototype.hasOwnProperty.call(); + ` + ] +}); From 47795fcc3b382eb2d70522ed88171338759e0e4c Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Tue, 26 Oct 2021 09:45:50 +0530 Subject: [PATCH 2/3] Update: Followup commit for the change required. --- lib/rules/prefer-object-has-own.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 437c64603e0..7a89907dedd 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -12,7 +12,7 @@ module.exports = { docs: { description: "disallow use of Object.prototype.hasOwnProperty and prefer use of Object.hasOwn", - recommended: "false", + recommended: false, url: "https://eslint.org/docs/rules/prefer-object-has-own" }, schema: [], From 7f15375b72ace628ecad6e227119527b72a7ede7 Mon Sep 17 00:00:00 2001 From: Gautam Arora Date: Thu, 28 Oct 2021 10:53:45 +0530 Subject: [PATCH 3/3] Update: Follow up commit. --- docs/rules/prefer-object-has-own.md | 7 ++++- lib/rules/index.js | 1 + lib/rules/prefer-object-has-own.js | 36 +++++++++++++++++------- tests/lib/rules/prefer-object-has-own.js | 27 ++++++++++++------ tools/rule-types.json | 1 + 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index f8a6f3dd957..c7a8ec3ef64 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -10,8 +10,9 @@ Examples of **incorrect** code for this rule: /*eslint prefer-object-has-own: "error"*/ Object.prototype.hasOwnProperty.call(obj, "a"); +({}).hasOwnProperty(obj,"a"); + let a = Object.prototype.hasOwnProperty; -a.call(obj, "a"); ``` Examples of **correct** code for this rule: @@ -21,3 +22,7 @@ Examples of **correct** code for this rule: Object.hasOwn(obj, "a"); ``` + +## Related Material + +[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) diff --git a/lib/rules/index.js b/lib/rules/index.js index ed322a4120a..130b635c972 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -255,6 +255,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"), "prefer-named-capture-group": () => require("./prefer-named-capture-group"), "prefer-numeric-literals": () => require("./prefer-numeric-literals"), + "prefer-object-has-own": () => require("./prefer-object-has-own"), "prefer-object-spread": () => require("./prefer-object-spread"), "prefer-promise-reject-errors": () => require("./prefer-promise-reject-errors"), "prefer-reflect": () => require("./prefer-reflect"), diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 7a89907dedd..639859acee8 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -6,6 +6,22 @@ "use strict"; +/** + * Checks to see if a property name object exists in the subtree recursively. + * @param {node} node to evalutate. + * @returns {boolean} `True` if object property exists, false otherwise. + */ +function checkForObject(node) { + if (!node.object) { + return false; + } + if (node.object.name === "Object") { + return true; + } + return checkForObject(node.object); +} + + module.exports = { meta: { type: "suggestion", @@ -17,24 +33,24 @@ module.exports = { }, schema: [], messages: { - useHasOwnMessage: - "Use Object.hasOwn instead of Object.prototype.hasOwnProperty." + useHasOwn: "Prefer using hasOwn property instead of hasOwnProperty." } }, create(context) { - - // declare the state of the rule return { MemberExpression(node) { + const propertyName = node.property.name; + const isObject = checkForObject(node); + const isObjectExpression = + node.object.type === "ObjectExpression"; + if ( - node.property.name === "hasOwnProperty" && - node.object.object.name === "Object" + propertyName === "hasOwnProperty" && + (isObject || isObjectExpression) ) { - const messageId = "useHasOwnMessage"; - context.report({ - messageId, - node + node, + messageId: "useHasOwn" }); } } diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 8b18e1c200a..2501822fb45 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -18,11 +18,11 @@ const { RuleTester } = require("../../../lib/rule-tester"); //------------------------------------------------------------------------------ const parserOptions = { - ecmaVersion: 2018, - sourceType: "module" + ecmaVersion: 2022 }; const ruleTester = new RuleTester({ parserOptions }); +const error = { messageId: "useHasOwn" }; ruleTester.run("prefer-object-has-own", rule, { valid: [ @@ -32,12 +32,21 @@ ruleTester.run("prefer-object-has-own", rule, { ` ], invalid: [ - ` - let a = Object.prototype.hasOwnProperty(); - obj.call(); - `, - ` - let a = Object.prototype.hasOwnProperty.call(); - ` + { + code: "Object.prototype.hasOwnProperty", + errors: [error] + }, + { + code: "Object.hasOwnProperty.call(obj, 'foo')", + errors: [error] + }, + { + code: "Object.prototype.hasOwnProperty.call(obj, 'foo')", + errors: [error] + }, + { + code: "({}).hasOwnProperty.call(obj, 'foo')", + errors: [error] + } ] }); diff --git a/tools/rule-types.json b/tools/rule-types.json index 4a71a8b09ed..235f398b818 100644 --- a/tools/rule-types.json +++ b/tools/rule-types.json @@ -242,6 +242,7 @@ "prefer-exponentiation-operator": "suggestion", "prefer-named-capture-group": "suggestion", "prefer-numeric-literals": "suggestion", + "prefer-object-has-own":"suggestion", "prefer-object-spread": "suggestion", "prefer-promise-reject-errors": "suggestion", "prefer-reflect": "suggestion",