diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md new file mode 100644 index 00000000000..c7a8ec3ef64 --- /dev/null +++ b/docs/rules/prefer-object-has-own.md @@ -0,0 +1,28 @@ +# 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"); + +({}).hasOwnProperty(obj,"a"); + +let a = Object.prototype.hasOwnProperty; +``` + +Examples of **correct** code for this rule: + +```js +/*eslint prefer-object-has-own: "error"*/ + +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 new file mode 100644 index 00000000000..639859acee8 --- /dev/null +++ b/lib/rules/prefer-object-has-own.js @@ -0,0 +1,59 @@ +/** + * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty + * @author Gautam Arora + * See LICENSE file in root directory for full license. + */ + +"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", + 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: { + useHasOwn: "Prefer using hasOwn property instead of hasOwnProperty." + } + }, + create(context) { + return { + MemberExpression(node) { + const propertyName = node.property.name; + const isObject = checkForObject(node); + const isObjectExpression = + node.object.type === "ObjectExpression"; + + if ( + propertyName === "hasOwnProperty" && + (isObject || isObjectExpression) + ) { + context.report({ + node, + messageId: "useHasOwn" + }); + } + } + }; + } +}; 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..2501822fb45 --- /dev/null +++ b/tests/lib/rules/prefer-object-has-own.js @@ -0,0 +1,52 @@ +/** + * @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: 2022 +}; + +const ruleTester = new RuleTester({ parserOptions }); +const error = { messageId: "useHasOwn" }; + +ruleTester.run("prefer-object-has-own", rule, { + valid: [ + ` + let obj = {}; + Object.hasOwn(obj,""); + ` + ], + invalid: [ + { + 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",