From c2aef447aab4ae616def7bbca71c1f1aa1e3b537 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Tue, 23 Nov 2021 19:44:45 +0530 Subject: [PATCH 01/28] feat: add `prefer-object-has-own` rule Co-authored-by: Gautam Arora --- docs/rules/prefer-object-has-own.md | 25 ++++++++++ lib/rules/index.js | 1 + lib/rules/prefer-object-has-own.js | 58 ++++++++++++++++++++++++ tests/lib/rules/prefer-object-has-own.js | 51 +++++++++++++++++++++ tools/rule-types.json | 3 +- 5 files changed, 137 insertions(+), 1 deletion(-) 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..41ee5d94b42 --- /dev/null +++ b/docs/rules/prefer-object-has-own.md @@ -0,0 +1,25 @@ +# 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..1516f26059b --- /dev/null +++ b/lib/rules/prefer-object-has-own.js @@ -0,0 +1,58 @@ +/** + * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty + * @author Nitin Kumar, Gautam Arora + */ + +"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..222e25e0136 --- /dev/null +++ b/tests/lib/rules/prefer-object-has-own.js @@ -0,0 +1,51 @@ +/** + * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty + * @author Nitin Kumar, Gautam Arora + */ + +"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..4885994d479 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", @@ -284,4 +285,4 @@ "wrap-regex": "layout", "yield-star-spacing": "layout", "yoda": "suggestion" -} +} \ No newline at end of file From cf62f3ad64f6071ba1aa089f0e1d8c46a76eedfb Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Tue, 23 Nov 2021 20:20:17 +0530 Subject: [PATCH 02/28] test: add more valid cases --- lib/rules/prefer-object-has-own.js | 2 +- tests/lib/rules/prefer-object-has-own.js | 72 ++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 1516f26059b..87e4e86e16c 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -8,7 +8,7 @@ /** * 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. + * @returns {boolean} `true` if object property exists, `false` otherwise. */ function checkForObject(node) { if (!node.object) { diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 222e25e0136..881f1f9fc50 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -25,16 +25,80 @@ const error = { messageId: "useHasOwn" }; ruleTester.run("prefer-object-has-own", rule, { valid: [ + "Object", + "Object(obj, prop)", + + /* + * "Object.hasOwnProperty", + * "Object.hasOwnProperty(prop)", + */ + "foo.hasOwnProperty(prop)", + "foo.hasOwnProperty(obj, prop)", + + // "Object.hasOwnProperty.call", + "foo.Object.hasOwnProperty.call(obj, prop)", + "foo.hasOwnProperty.call(obj, prop)", + "Object.foo.call(obj, prop)", + + /* + * "Object.hasOwnProperty.foo(obj, prop)", + * "Object.hasOwnProperty.call.foo(obj, prop)", + * "Object[hasOwnProperty].call(obj, prop)", + * "Object.hasOwnProperty[call](obj, prop)", + * "class C { #hasOwnProperty; foo() { Object.#hasOwnProperty.call(obj, prop) } }", + * "class C { #call; foo() { Object.hasOwnProperty.#call(obj, prop) } }", + * "(Object) => Object.hasOwnProperty.call(obj, prop)", // not global Object + */ + "Object.prototype", + "Object.prototype(obj, prop)", + + /* + * "Object.prototype.hasOwnProperty", + * "Object.prototype.hasOwnProperty(obj, prop)", + * "Object.prototype.hasOwnProperty.call", + */ + "foo.Object.prototype.hasOwnProperty.call(obj, prop)", + "foo.prototype.hasOwnProperty.call(obj, prop)", + + // "Object.foo.hasOwnProperty.call(obj, prop)", + "Object.prototype.foo.call(obj, prop)", + + /* + * "Object.prototype.hasOwnProperty.foo(obj, prop)", + * "Object.prototype.hasOwnProperty.call.foo(obj, prop)", + * "Object[prototype].hasOwnProperty.call(obj, prop)", + * "Object.prototype[hasOwnProperty].call(obj, prop)", + * "Object.prototype.hasOwnProperty[call](obj, prop)", + * "class C { #prototype; foo() { Object.#prototype.hasOwnProperty.call(obj, prop) } }", + * "class C { #hasOwnProperty; foo() { Object.prototype.#hasOwnProperty.call(obj, prop) } }", + * "class C { #call; foo() { Object.prototype.hasOwnProperty.#call(obj, prop) } }", + * "(Object) => Object.prototype.hasOwnProperty.call(obj, prop)", // not global Object + */ + "({})", + "({}(obj, prop))", + + /* + * "({}.hasOwnProperty)", + * "({}.hasOwnProperty(prop))", + * "({}.hasOwnProperty(obj, prop))", + * "({}.hasOwnProperty.call)", + */ + "({}.foo.call(obj, prop))", + + /* + * "({}.hasOwnProperty.foo(obj, prop))", + * "({}[hasOwnProperty].call(obj, prop))", + * "({}.hasOwnProperty[call](obj, prop))", + * "class C { #hasOwnProperty; foo() { ({}.#hasOwnProperty.call(obj, prop)) } }", + * "class C { #call; foo() { ({}.hasOwnProperty.#call(obj, prop)) } }", + * "({ foo }.hasOwnProperty.call(obj, prop))", // object literal should be empty + */ ` let obj = {}; Object.hasOwn(obj,""); ` ], invalid: [ - { - code: "Object.prototype.hasOwnProperty", - errors: [error] - }, { code: "Object.hasOwnProperty.call(obj, 'foo')", errors: [error] From f5d1ad53c72724101603d80d2999d83e0e6bb02e Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Wed, 24 Nov 2021 11:31:01 +0530 Subject: [PATCH 03/28] fix: cover more cases --- lib/rules/prefer-object-has-own.js | 23 +++++--- tests/lib/rules/prefer-object-has-own.js | 73 ++++++++++-------------- 2 files changed, 43 insertions(+), 53 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 87e4e86e16c..76d4b2f45bd 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -5,6 +5,12 @@ "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + /** * Checks to see if a property name object exists in the subtree recursively. * @param {node} node to evalutate. @@ -14,13 +20,12 @@ function checkForObject(node) { if (!node.object) { return false; } - if (node.object.name === "Object") { + if (node.object.name === "Object" || (node.object.type === "ObjectExpression" && node.object.properties.length === 0)) { return true; } return checkForObject(node.object); } - module.exports = { meta: { type: "suggestion", @@ -37,15 +42,15 @@ module.exports = { }, create(context) { return { - MemberExpression(node) { - const propertyName = node.property.name; - const isObject = checkForObject(node); - const isObjectExpression = - node.object.type === "ObjectExpression"; + CallExpression(node) { + const calleePropertyName = astUtils.getStaticPropertyName(node.callee); + const isObject = checkForObject(node.callee); + const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); if ( - propertyName === "hasOwnProperty" && - (isObject || isObjectExpression) + calleePropertyName === "call" && + objectPropertyName === "hasOwnProperty" && + isObject ) { context.report({ node, diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 881f1f9fc50..c21aaa6d400 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -27,72 +27,57 @@ ruleTester.run("prefer-object-has-own", rule, { valid: [ "Object", "Object(obj, prop)", - - /* - * "Object.hasOwnProperty", - * "Object.hasOwnProperty(prop)", - */ + "Object.hasOwnProperty", + "Object.hasOwnProperty(prop)", "foo.hasOwnProperty(prop)", "foo.hasOwnProperty(obj, prop)", - - // "Object.hasOwnProperty.call", + "Object.hasOwnProperty.call", "foo.Object.hasOwnProperty.call(obj, prop)", "foo.hasOwnProperty.call(obj, prop)", "Object.foo.call(obj, prop)", + "Object.hasOwnProperty.foo(obj, prop)", + "Object.hasOwnProperty.call.foo(obj, prop)", + "Object[hasOwnProperty].call(obj, prop)", + "Object.hasOwnProperty[call](obj, prop)", + "class C { #hasOwnProperty; foo() { Object.#hasOwnProperty.call(obj, prop) } }", + "class C { #call; foo() { Object.hasOwnProperty.#call(obj, prop) } }", - /* - * "Object.hasOwnProperty.foo(obj, prop)", - * "Object.hasOwnProperty.call.foo(obj, prop)", - * "Object[hasOwnProperty].call(obj, prop)", - * "Object.hasOwnProperty[call](obj, prop)", - * "class C { #hasOwnProperty; foo() { Object.#hasOwnProperty.call(obj, prop) } }", - * "class C { #call; foo() { Object.hasOwnProperty.#call(obj, prop) } }", - * "(Object) => Object.hasOwnProperty.call(obj, prop)", // not global Object - */ + // "(Object) => Object.hasOwnProperty.call(obj, prop)", // not global Object "Object.prototype", "Object.prototype(obj, prop)", - - /* - * "Object.prototype.hasOwnProperty", - * "Object.prototype.hasOwnProperty(obj, prop)", - * "Object.prototype.hasOwnProperty.call", - */ + "Object.prototype.hasOwnProperty", + "Object.prototype.hasOwnProperty(obj, prop)", + "Object.prototype.hasOwnProperty.call", "foo.Object.prototype.hasOwnProperty.call(obj, prop)", "foo.prototype.hasOwnProperty.call(obj, prop)", // "Object.foo.hasOwnProperty.call(obj, prop)", "Object.prototype.foo.call(obj, prop)", + "Object.prototype.hasOwnProperty.foo(obj, prop)", + "Object.prototype.hasOwnProperty.call.foo(obj, prop)", + "Object.prototype[hasOwnProperty].call(obj, prop)", + "Object.prototype.hasOwnProperty[call](obj, prop)", + "class C { #hasOwnProperty; foo() { Object.prototype.#hasOwnProperty.call(obj, prop) } }", + "class C { #call; foo() { Object.prototype.hasOwnProperty.#call(obj, prop) } }", /* - * "Object.prototype.hasOwnProperty.foo(obj, prop)", - * "Object.prototype.hasOwnProperty.call.foo(obj, prop)", * "Object[prototype].hasOwnProperty.call(obj, prop)", - * "Object.prototype[hasOwnProperty].call(obj, prop)", - * "Object.prototype.hasOwnProperty[call](obj, prop)", * "class C { #prototype; foo() { Object.#prototype.hasOwnProperty.call(obj, prop) } }", - * "class C { #hasOwnProperty; foo() { Object.prototype.#hasOwnProperty.call(obj, prop) } }", - * "class C { #call; foo() { Object.prototype.hasOwnProperty.#call(obj, prop) } }", * "(Object) => Object.prototype.hasOwnProperty.call(obj, prop)", // not global Object */ "({})", "({}(obj, prop))", - - /* - * "({}.hasOwnProperty)", - * "({}.hasOwnProperty(prop))", - * "({}.hasOwnProperty(obj, prop))", - * "({}.hasOwnProperty.call)", - */ + "({}.hasOwnProperty)", + "({}.hasOwnProperty(prop))", + "({}.hasOwnProperty(obj, prop))", + "({}.hasOwnProperty.call)", "({}.foo.call(obj, prop))", - - /* - * "({}.hasOwnProperty.foo(obj, prop))", - * "({}[hasOwnProperty].call(obj, prop))", - * "({}.hasOwnProperty[call](obj, prop))", - * "class C { #hasOwnProperty; foo() { ({}.#hasOwnProperty.call(obj, prop)) } }", - * "class C { #call; foo() { ({}.hasOwnProperty.#call(obj, prop)) } }", - * "({ foo }.hasOwnProperty.call(obj, prop))", // object literal should be empty - */ + "({}.hasOwnProperty.foo(obj, prop))", + "({}[hasOwnProperty].call(obj, prop))", + "({}.hasOwnProperty[call](obj, prop))", + "class C { #hasOwnProperty; foo() { ({}.#hasOwnProperty.call(obj, prop)) } }", + "class C { #call; foo() { ({}.hasOwnProperty.#call(obj, prop)) } }", + "({ foo }.hasOwnProperty.call(obj, prop))", // object literal should be empty ` let obj = {}; Object.hasOwn(obj,""); From 0576a3c03e48754cfab9896785161668f877f41f Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Wed, 24 Nov 2021 11:42:22 +0530 Subject: [PATCH 04/28] chore: add jsdoc type annotation --- lib/rules/prefer-object-has-own.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 76d4b2f45bd..3bc15b4564a 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -26,6 +26,11 @@ function checkForObject(node) { return checkForObject(node.object); } +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** @type {import('../shared/types').Rule} */ module.exports = { meta: { type: "suggestion", From 377ca24b1c080693a1ee48b712a867c450d1e2f2 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 25 Nov 2021 07:29:08 +0530 Subject: [PATCH 05/28] fix: cover more cases --- lib/rules/prefer-object-has-own.js | 16 ++++++++++++++-- tests/lib/rules/prefer-object-has-own.js | 7 +++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 3bc15b4564a..2f5edd0a9f4 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -20,10 +20,22 @@ function checkForObject(node) { if (!node.object) { return false; } + + /* + * Object.hasOwnProperty.call(obj, prop) or ({}).hasOwnProperty.call(obj, prop) - `true` + * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty + */ if (node.object.name === "Object" || (node.object.type === "ObjectExpression" && node.object.properties.length === 0)) { return true; } - return checkForObject(node.object); + + const propertyName = astUtils.getStaticPropertyName(node.object); + + if (propertyName === "hasOwnProperty" || propertyName === "prototype") { + return checkForObject(node.object); + } + + return false; } //------------------------------------------------------------------------------ @@ -49,8 +61,8 @@ module.exports = { return { CallExpression(node) { const calleePropertyName = astUtils.getStaticPropertyName(node.callee); - const isObject = checkForObject(node.callee); const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); + const isObject = checkForObject(node.callee); if ( calleePropertyName === "call" && diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index c21aaa6d400..7da30a38687 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -50,8 +50,7 @@ ruleTester.run("prefer-object-has-own", rule, { "Object.prototype.hasOwnProperty.call", "foo.Object.prototype.hasOwnProperty.call(obj, prop)", "foo.prototype.hasOwnProperty.call(obj, prop)", - - // "Object.foo.hasOwnProperty.call(obj, prop)", + "Object.foo.hasOwnProperty.call(obj, prop)", "Object.prototype.foo.call(obj, prop)", "Object.prototype.hasOwnProperty.foo(obj, prop)", "Object.prototype.hasOwnProperty.call.foo(obj, prop)", @@ -59,10 +58,10 @@ ruleTester.run("prefer-object-has-own", rule, { "Object.prototype.hasOwnProperty[call](obj, prop)", "class C { #hasOwnProperty; foo() { Object.prototype.#hasOwnProperty.call(obj, prop) } }", "class C { #call; foo() { Object.prototype.hasOwnProperty.#call(obj, prop) } }", + "Object[prototype].hasOwnProperty.call(obj, prop)", + "class C { #prototype; foo() { Object.#prototype.hasOwnProperty.call(obj, prop) } }", /* - * "Object[prototype].hasOwnProperty.call(obj, prop)", - * "class C { #prototype; foo() { Object.#prototype.hasOwnProperty.call(obj, prop) } }", * "(Object) => Object.prototype.hasOwnProperty.call(obj, prop)", // not global Object */ "({})", From 03de5a0d831cf561e483c1251ce61475b463b251 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 25 Nov 2021 07:42:25 +0530 Subject: [PATCH 06/28] test: add more invalid test cases --- tests/lib/rules/prefer-object-has-own.js | 54 +++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 7da30a38687..0c4018c0898 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -34,6 +34,7 @@ ruleTester.run("prefer-object-has-own", rule, { "Object.hasOwnProperty.call", "foo.Object.hasOwnProperty.call(obj, prop)", "foo.hasOwnProperty.call(obj, prop)", + "foo.call(Object.prototype.hasOwnProperty, Object.prototype.hasOwnProperty.call)", "Object.foo.call(obj, prop)", "Object.hasOwnProperty.foo(obj, prop)", "Object.hasOwnProperty.call.foo(obj, prop)", @@ -74,13 +75,16 @@ ruleTester.run("prefer-object-has-own", rule, { "({}.hasOwnProperty.foo(obj, prop))", "({}[hasOwnProperty].call(obj, prop))", "({}.hasOwnProperty[call](obj, prop))", + "({}).hasOwnProperty[call](object, property)", + "({})[hasOwnProperty].call(object, property)", "class C { #hasOwnProperty; foo() { ({}.#hasOwnProperty.call(obj, prop)) } }", "class C { #call; foo() { ({}.hasOwnProperty.#call(obj, prop)) } }", "({ foo }.hasOwnProperty.call(obj, prop))", // object literal should be empty ` let obj = {}; Object.hasOwn(obj,""); - ` + `, + "const hasProperty = Object.hasOwn(object, property);" ], invalid: [ { @@ -94,6 +98,54 @@ ruleTester.run("prefer-object-has-own", rule, { { code: "({}).hasOwnProperty.call(obj, 'foo')", errors: [error] + }, + { + code: "const hasProperty = Object.prototype.hasOwnProperty.call(object, property);", + errors: [error] + }, + { + code: "const hasProperty = (( Object.prototype.hasOwnProperty.call(object, property) ));", + errors: [error] + }, + { + code: "const hasProperty = (( Object.prototype.hasOwnProperty.call ))(object, property);", + errors: [error] + }, + { + code: "const hasProperty = (( Object.prototype.hasOwnProperty )).call(object, property);", + errors: [error] + }, + { + code: "const hasProperty = (( Object.prototype )).hasOwnProperty.call(object, property);", + errors: [error] + }, + { + code: "const hasProperty = (( Object )).prototype.hasOwnProperty.call(object, property);", + errors: [error] + }, + { + code: "const hasProperty = {}.hasOwnProperty.call(object, property);", + errors: [error] + }, + { + code: "const hasProperty = (( {}.hasOwnProperty.call(object, property) ));", + errors: [error] + }, + { + code: "const hasProperty = (( {}.hasOwnProperty.call ))(object, property);", + errors: [error] + }, + { + code: "const hasProperty = (( {}.hasOwnProperty )).call(object, property);", + errors: [error] + }, + { + code: "const hasProperty = (( {} )).hasOwnProperty.call(object, property);", + errors: [error] + }, + { + code: "function foo(){return{}.hasOwnProperty.call(object, property)}", + errors: [error] } ] }); From f58dda76591e815ba6de7f7bb89cdd7869f8a089 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 25 Nov 2021 17:17:02 +0530 Subject: [PATCH 07/28] fix: improve meta data --- lib/rules/prefer-object-has-own.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 2f5edd0a9f4..ac10841fd22 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -48,13 +48,13 @@ module.exports = { type: "suggestion", docs: { description: - "disallow use of Object.prototype.hasOwnProperty and prefer use of Object.hasOwn", + "disallow use of Object.prototype.hasOwnProperty.call(…) 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." + useHasOwn: "Prefer using Object.hasOwn(…) over Object.prototype.hasOwnProperty.call(…)." } }, create(context) { From b7098ebcf0a3c05eeb7f74963b09e1fba82e1bd0 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 25 Nov 2021 17:24:03 +0530 Subject: [PATCH 08/28] test: add assertions for location --- tests/lib/rules/prefer-object-has-own.js | 121 ++++++++++++++++++++--- 1 file changed, 105 insertions(+), 16 deletions(-) diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 0c4018c0898..baaf249edae 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -21,7 +21,6 @@ const parserOptions = { }; const ruleTester = new RuleTester({ parserOptions }); -const error = { messageId: "useHasOwn" }; ruleTester.run("prefer-object-has-own", rule, { valid: [ @@ -89,63 +88,153 @@ ruleTester.run("prefer-object-has-own", rule, { invalid: [ { code: "Object.hasOwnProperty.call(obj, 'foo')", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 39 + }] }, { code: "Object.prototype.hasOwnProperty.call(obj, 'foo')", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 49 + }] }, { code: "({}).hasOwnProperty.call(obj, 'foo')", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 37 + }] }, { code: "const hasProperty = Object.prototype.hasOwnProperty.call(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 75 + }] }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty.call(object, property) ));", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 24, + endLine: 1, + endColumn: 78 + }] }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty.call ))(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 81 + }] }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty )).call(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 81 + }] }, { code: "const hasProperty = (( Object.prototype )).hasOwnProperty.call(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 81 + }] }, { code: "const hasProperty = (( Object )).prototype.hasOwnProperty.call(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 81 + }] }, { code: "const hasProperty = {}.hasOwnProperty.call(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 61 + }] }, { code: "const hasProperty = (( {}.hasOwnProperty.call(object, property) ));", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 24, + endLine: 1, + endColumn: 64 + }] }, { code: "const hasProperty = (( {}.hasOwnProperty.call ))(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 67 + }] }, { code: "const hasProperty = (( {}.hasOwnProperty )).call(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 67 + }] }, { code: "const hasProperty = (( {} )).hasOwnProperty.call(object, property);", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 21, + endLine: 1, + endColumn: 67 + }] }, { code: "function foo(){return{}.hasOwnProperty.call(object, property)}", - errors: [error] + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 22, + endLine: 1, + endColumn: 62 + }] } ] }); From c12181edeadad3472d40f282f107649dabfb0922 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 25 Nov 2021 17:34:59 +0530 Subject: [PATCH 09/28] docs: update `prefer-object-has-own` --- docs/rules/prefer-object-has-own.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 41ee5d94b42..548821f0062 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -1,6 +1,8 @@ -# Prefer use of Object.hasOwn over `Object.prototype.hasOwnPrototype` (prefer-object-has-own) +# Prefer `Object.hasOwn(…)` over `Object.prototype.hasOwnProperty.call(…)` -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. +`Object.hasOwn(…)` is more accessible than `Object.prototype.hasOwnProperty.call(…)`. + +It is recommended over Object.hasOwnProperty() because it works for objects created using Object.create(null) and with objects that have overridden the inherited hasOwnProperty() method. ## Rule Details @@ -8,16 +10,22 @@ 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; + +({}).hasOwnProperty(obj, "a"); + +const hasProperty = Object.prototype.hasOwnProperty.call(object, property); ``` Examples of **correct** code for this rule: ```js /*eslint prefer-object-has-own: "error"*/ + Object.hasOwn(obj, "a"); + +const hasProperty = Object.hasOwn(object, property); ``` ## Related Material From b955073d4758b792918c4c1aef0a03ee6e551670 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 25 Nov 2021 17:53:16 +0530 Subject: [PATCH 10/28] fix: report for Object with global scope only --- lib/rules/prefer-object-has-own.js | 7 ++++++- tests/lib/rules/prefer-object-has-own.js | 8 ++------ tools/rule-types.json | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index ac10841fd22..f6bc53f6808 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -64,10 +64,15 @@ module.exports = { const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); const isObject = checkForObject(node.callee); + // check `Object` + const scope = context.getScope(); + const variable = astUtils.getVariableByName(scope, "Object"); + if ( calleePropertyName === "call" && objectPropertyName === "hasOwnProperty" && - isObject + isObject && + variable.scope.type === "global" ) { context.report({ node, diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index baaf249edae..b8bea31248e 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -41,8 +41,7 @@ ruleTester.run("prefer-object-has-own", rule, { "Object.hasOwnProperty[call](obj, prop)", "class C { #hasOwnProperty; foo() { Object.#hasOwnProperty.call(obj, prop) } }", "class C { #call; foo() { Object.hasOwnProperty.#call(obj, prop) } }", - - // "(Object) => Object.hasOwnProperty.call(obj, prop)", // not global Object + "(Object) => Object.hasOwnProperty.call(obj, prop)", // not global Object "Object.prototype", "Object.prototype(obj, prop)", "Object.prototype.hasOwnProperty", @@ -60,10 +59,7 @@ ruleTester.run("prefer-object-has-own", rule, { "class C { #call; foo() { Object.prototype.hasOwnProperty.#call(obj, prop) } }", "Object[prototype].hasOwnProperty.call(obj, prop)", "class C { #prototype; foo() { Object.#prototype.hasOwnProperty.call(obj, prop) } }", - - /* - * "(Object) => Object.prototype.hasOwnProperty.call(obj, prop)", // not global Object - */ + "(Object) => Object.prototype.hasOwnProperty.call(obj, prop)", // not global Object "({})", "({}(obj, prop))", "({}.hasOwnProperty)", diff --git a/tools/rule-types.json b/tools/rule-types.json index 4885994d479..85484c49210 100644 --- a/tools/rule-types.json +++ b/tools/rule-types.json @@ -285,4 +285,4 @@ "wrap-regex": "layout", "yield-star-spacing": "layout", "yoda": "suggestion" -} \ No newline at end of file +} From b738771f8a4759529a8e8daa2be78eb284f1fc03 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 25 Nov 2021 17:54:32 +0530 Subject: [PATCH 11/28] docs: add rule id --- docs/rules/prefer-object-has-own.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 548821f0062..782e9e8159e 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -1,4 +1,4 @@ -# Prefer `Object.hasOwn(…)` over `Object.prototype.hasOwnProperty.call(…)` +# Prefer `Object.hasOwn(…)` over `Object.prototype.hasOwnProperty.call(…)` (prefer-object-has-own) `Object.hasOwn(…)` is more accessible than `Object.prototype.hasOwnProperty.call(…)`. From c3ac8246b7a6899e790f80582d4eb6d58ed3f529 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 2 Dec 2021 18:38:31 +0530 Subject: [PATCH 12/28] feat: add fixer for `prefer-object-has-own` rule --- docs/rules/prefer-object-has-own.md | 2 +- lib/rules/prefer-object-has-own.js | 10 ++++++-- tests/lib/rules/prefer-object-has-own.js | 32 +++++++++++++++++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 782e9e8159e..942a405df7f 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -30,4 +30,4 @@ const hasProperty = Object.hasOwn(object, property); ## Related Material -[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) +[MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index f6bc53f6808..21b04037bac 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -55,7 +55,8 @@ module.exports = { schema: [], messages: { useHasOwn: "Prefer using Object.hasOwn(…) over Object.prototype.hasOwnProperty.call(…)." - } + }, + fixable: "code" }, create(context) { return { @@ -76,7 +77,12 @@ module.exports = { ) { context.report({ node, - messageId: "useHasOwn" + messageId: "useHasOwn", + *fix(fixer) { + yield fixer.replaceText(node.callee, "Object.hasOwn"); + + yield fixer.replaceTextRange([node.arguments[0].range[0], node.arguments[1].range[0]], ""); + } }); } } diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index b8bea31248e..c1bb4112395 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -84,6 +84,7 @@ ruleTester.run("prefer-object-has-own", rule, { invalid: [ { code: "Object.hasOwnProperty.call(obj, 'foo')", + output: "Object.hasOwn('foo')", errors: [{ messageId: "useHasOwn", line: 1, @@ -92,8 +93,20 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 39 }] }, + { + code: "Object.hasOwnProperty.call(obj, property)", + output: "Object.hasOwn(property)", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 42 + }] + }, { code: "Object.prototype.hasOwnProperty.call(obj, 'foo')", + output: "Object.hasOwn('foo')", errors: [{ messageId: "useHasOwn", line: 1, @@ -104,6 +117,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "({}).hasOwnProperty.call(obj, 'foo')", + output: "Object.hasOwn('foo')", errors: [{ messageId: "useHasOwn", line: 1, @@ -114,6 +128,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = Object.prototype.hasOwnProperty.call(object, property);", + output: "const hasProperty = Object.hasOwn(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -124,6 +139,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty.call(object, property) ));", + output: "const hasProperty = (( Object.hasOwn(property) ));", errors: [{ messageId: "useHasOwn", line: 1, @@ -134,6 +150,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty.call ))(object, property);", + output: "const hasProperty = (( Object.hasOwn ))(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -144,6 +161,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty )).call(object, property);", + output: "const hasProperty = Object.hasOwn(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -154,6 +172,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype )).hasOwnProperty.call(object, property);", + output: "const hasProperty = Object.hasOwn(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -164,6 +183,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object )).prototype.hasOwnProperty.call(object, property);", + output: "const hasProperty = Object.hasOwn(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -174,6 +194,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = {}.hasOwnProperty.call(object, property);", + output: "const hasProperty = Object.hasOwn(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -184,6 +205,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {}.hasOwnProperty.call(object, property) ));", + output: "const hasProperty = (( Object.hasOwn(property) ));", errors: [{ messageId: "useHasOwn", line: 1, @@ -194,6 +216,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {}.hasOwnProperty.call ))(object, property);", + output: "const hasProperty = (( Object.hasOwn ))(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -204,6 +227,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {}.hasOwnProperty )).call(object, property);", + output: "const hasProperty = Object.hasOwn(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -214,6 +238,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {} )).hasOwnProperty.call(object, property);", + output: "const hasProperty = Object.hasOwn(property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -223,13 +248,14 @@ ruleTester.run("prefer-object-has-own", rule, { }] }, { - code: "function foo(){return{}.hasOwnProperty.call(object, property)}", + code: "function foo(){return {}.hasOwnProperty.call(object, property)}", + output: "function foo(){return Object.hasOwn(property)}", errors: [{ messageId: "useHasOwn", line: 1, - column: 22, + column: 23, endLine: 1, - endColumn: 62 + endColumn: 63 }] } ] From e14c30407590f56830cbc6503f9fe87888290822 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 2 Dec 2021 18:41:10 +0530 Subject: [PATCH 13/28] chore: udpate comment --- lib/rules/prefer-object-has-own.js | 4 +-- tests/lib/rules/prefer-object-has-own.js | 32 ++++++++++++------------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 21b04037bac..c066876c059 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -65,7 +65,7 @@ module.exports = { const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); const isObject = checkForObject(node.callee); - // check `Object` + // check `Object` scope const scope = context.getScope(); const variable = astUtils.getVariableByName(scope, "Object"); @@ -80,8 +80,6 @@ module.exports = { messageId: "useHasOwn", *fix(fixer) { yield fixer.replaceText(node.callee, "Object.hasOwn"); - - yield fixer.replaceTextRange([node.arguments[0].range[0], node.arguments[1].range[0]], ""); } }); } diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index c1bb4112395..e146de5265c 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -84,7 +84,7 @@ ruleTester.run("prefer-object-has-own", rule, { invalid: [ { code: "Object.hasOwnProperty.call(obj, 'foo')", - output: "Object.hasOwn('foo')", + output: "Object.hasOwn(obj, 'foo')", errors: [{ messageId: "useHasOwn", line: 1, @@ -95,7 +95,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "Object.hasOwnProperty.call(obj, property)", - output: "Object.hasOwn(property)", + output: "Object.hasOwn(obj, property)", errors: [{ messageId: "useHasOwn", line: 1, @@ -106,7 +106,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "Object.prototype.hasOwnProperty.call(obj, 'foo')", - output: "Object.hasOwn('foo')", + output: "Object.hasOwn(obj, 'foo')", errors: [{ messageId: "useHasOwn", line: 1, @@ -117,7 +117,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "({}).hasOwnProperty.call(obj, 'foo')", - output: "Object.hasOwn('foo')", + output: "Object.hasOwn(obj, 'foo')", errors: [{ messageId: "useHasOwn", line: 1, @@ -128,7 +128,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = Object.prototype.hasOwnProperty.call(object, property);", - output: "const hasProperty = Object.hasOwn(property);", + output: "const hasProperty = Object.hasOwn(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -139,7 +139,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty.call(object, property) ));", - output: "const hasProperty = (( Object.hasOwn(property) ));", + output: "const hasProperty = (( Object.hasOwn(object, property) ));", errors: [{ messageId: "useHasOwn", line: 1, @@ -150,7 +150,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty.call ))(object, property);", - output: "const hasProperty = (( Object.hasOwn ))(property);", + output: "const hasProperty = (( Object.hasOwn ))(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -161,7 +161,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype.hasOwnProperty )).call(object, property);", - output: "const hasProperty = Object.hasOwn(property);", + output: "const hasProperty = Object.hasOwn(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -172,7 +172,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object.prototype )).hasOwnProperty.call(object, property);", - output: "const hasProperty = Object.hasOwn(property);", + output: "const hasProperty = Object.hasOwn(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -183,7 +183,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( Object )).prototype.hasOwnProperty.call(object, property);", - output: "const hasProperty = Object.hasOwn(property);", + output: "const hasProperty = Object.hasOwn(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -194,7 +194,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = {}.hasOwnProperty.call(object, property);", - output: "const hasProperty = Object.hasOwn(property);", + output: "const hasProperty = Object.hasOwn(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -205,7 +205,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {}.hasOwnProperty.call(object, property) ));", - output: "const hasProperty = (( Object.hasOwn(property) ));", + output: "const hasProperty = (( Object.hasOwn(object, property) ));", errors: [{ messageId: "useHasOwn", line: 1, @@ -216,7 +216,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {}.hasOwnProperty.call ))(object, property);", - output: "const hasProperty = (( Object.hasOwn ))(property);", + output: "const hasProperty = (( Object.hasOwn ))(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -227,7 +227,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {}.hasOwnProperty )).call(object, property);", - output: "const hasProperty = Object.hasOwn(property);", + output: "const hasProperty = Object.hasOwn(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -238,7 +238,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "const hasProperty = (( {} )).hasOwnProperty.call(object, property);", - output: "const hasProperty = Object.hasOwn(property);", + output: "const hasProperty = Object.hasOwn(object, property);", errors: [{ messageId: "useHasOwn", line: 1, @@ -249,7 +249,7 @@ ruleTester.run("prefer-object-has-own", rule, { }, { code: "function foo(){return {}.hasOwnProperty.call(object, property)}", - output: "function foo(){return Object.hasOwn(property)}", + output: "function foo(){return Object.hasOwn(object, property)}", errors: [{ messageId: "useHasOwn", line: 1, From b095205240d2c26a5a32a2ab06a4839bb394414e Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Fri, 3 Dec 2021 07:32:53 +0530 Subject: [PATCH 14/28] chore: apply suggestions --- lib/rules/prefer-object-has-own.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index c066876c059..7455eed67a4 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -1,6 +1,7 @@ /** * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty - * @author Nitin Kumar, Gautam Arora + * @author Nitin Kumar + * @author Gautam Arora */ "use strict"; @@ -13,10 +14,10 @@ const astUtils = require("./utils/ast-utils"); /** * Checks to see if a property name object exists in the subtree recursively. - * @param {node} node to evalutate. + * @param {ASTnode} node to evalutate. * @returns {boolean} `true` if object property exists, `false` otherwise. */ -function checkForObject(node) { +function hasLeftHandObject(node) { if (!node.object) { return false; } @@ -32,7 +33,7 @@ function checkForObject(node) { const propertyName = astUtils.getStaticPropertyName(node.object); if (propertyName === "hasOwnProperty" || propertyName === "prototype") { - return checkForObject(node.object); + return hasLeftHandObject(node.object); } return false; @@ -63,7 +64,7 @@ module.exports = { CallExpression(node) { const calleePropertyName = astUtils.getStaticPropertyName(node.callee); const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); - const isObject = checkForObject(node.callee); + const isObject = hasLeftHandObject(node.callee); // check `Object` scope const scope = context.getScope(); From a26dd155878a7e08edc2953b9e0b3e9f35a77d9f Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Fri, 3 Dec 2021 07:34:46 +0530 Subject: [PATCH 15/28] docs: udpate --- docs/rules/prefer-object-has-own.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 942a405df7f..35614d878b3 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -2,7 +2,7 @@ `Object.hasOwn(…)` is more accessible than `Object.prototype.hasOwnProperty.call(…)`. -It is recommended over Object.hasOwnProperty() because it works for objects created using Object.create(null) and with objects that have overridden the inherited hasOwnProperty() method. +It is recommended over `Object#hasOwnProperty()` because it works for objects created using `Object.create(null)` and with objects that have overridden the inherited `hasOwnProperty()` method. ## Rule Details @@ -30,4 +30,4 @@ const hasProperty = Object.hasOwn(object, property); ## Related Material -[MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) +* [Object.hasOwn()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) From f70a4b1463d57b254553740751680aa1995d8cc5 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Fri, 3 Dec 2021 08:03:53 +0530 Subject: [PATCH 16/28] docs: add example --- docs/rules/prefer-object-has-own.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 35614d878b3..0b11aeed4bf 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -4,6 +4,22 @@ It is recommended over `Object#hasOwnProperty()` because it works for objects created using `Object.create(null)` and with objects that have overridden the inherited `hasOwnProperty()` method. +```js +const foo = { + hasOwnProperty: function() { + return false; + }, + bar: 'Hello World' +}; + +console.log(Object.hasOwn(foo, 'bar')); // true - remplementation of hasOwnProperty() does not affect Object + +const bar = Object.create(null); +bar.prop = 'exists'; + +console.log(Object.hasOwn(bar, 'prop')); // true - works irrespective of how the object is created. +``` + ## Rule Details Examples of **incorrect** code for this rule: From ae1fb0f5f2271ae3128bc1032f0fbff2babea6c1 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Fri, 3 Dec 2021 14:49:08 +0530 Subject: [PATCH 17/28] chore: update comment --- tests/lib/rules/prefer-object-has-own.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index e146de5265c..65600cb25e1 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -1,6 +1,7 @@ /** * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty - * @author Nitin Kumar, Gautam Arora + * @author Nitin Kumar + * @author Gautam Arora */ "use strict"; From 6c279152f5009f02f4af5c7fca1d100b2634c2a5 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Sat, 4 Dec 2021 07:33:55 +0530 Subject: [PATCH 18/28] test: add another valid test case --- tests/lib/rules/prefer-object-has-own.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 65600cb25e1..d2f8da98e91 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -29,6 +29,7 @@ ruleTester.run("prefer-object-has-own", rule, { "Object(obj, prop)", "Object.hasOwnProperty", "Object.hasOwnProperty(prop)", + "hasOwnProperty(obj, prop)", "foo.hasOwnProperty(prop)", "foo.hasOwnProperty(obj, prop)", "Object.hasOwnProperty.call", From c9406bb1e8462d178c772c8e89be0376fe9e0af4 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Sat, 4 Dec 2021 07:34:41 +0530 Subject: [PATCH 19/28] docs: fix typo --- docs/rules/prefer-object-has-own.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 0b11aeed4bf..54e7e843c22 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -12,7 +12,7 @@ const foo = { bar: 'Hello World' }; -console.log(Object.hasOwn(foo, 'bar')); // true - remplementation of hasOwnProperty() does not affect Object +console.log(Object.hasOwn(foo, 'bar')); // true - re-implementation of hasOwnProperty() does not affect Object const bar = Object.create(null); bar.prop = 'exists'; From f2b219add9a3857b5efb779ddd3223730bdb1721 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Tue, 7 Dec 2021 07:25:28 +0530 Subject: [PATCH 20/28] fix: improve autofix --- lib/rules/prefer-object-has-own.js | 12 +++++++++--- tests/lib/rules/prefer-object-has-own.js | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 7455eed67a4..353e3d66d67 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -74,13 +74,19 @@ module.exports = { calleePropertyName === "call" && objectPropertyName === "hasOwnProperty" && isObject && - variable.scope.type === "global" + variable && variable.scope.type === "global" ) { context.report({ node, messageId: "useHasOwn", - *fix(fixer) { - yield fixer.replaceText(node.callee, "Object.hasOwn"); + fix(fixer) { + const sourceCode = context.getSourceCode(node); + + if (sourceCode.getCommentsInside(node.callee).length > 0) { + return null; + } + + return fixer.replaceText(node.callee, "Object.hasOwn"); } }); } diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index d2f8da98e91..8aef07f0ba4 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -81,7 +81,9 @@ ruleTester.run("prefer-object-has-own", rule, { let obj = {}; Object.hasOwn(obj,""); `, - "const hasProperty = Object.hasOwn(object, property);" + "const hasProperty = Object.hasOwn(object, property);", + `/* global Object: off */ + ({}).hasOwnProperty.call(a, b);` ], invalid: [ { @@ -128,6 +130,19 @@ ruleTester.run("prefer-object-has-own", rule, { endColumn: 37 }] }, + + // prevent autofixing if there are any comments + { + code: "Object/* comment */.prototype.hasOwnProperty.call(a, b);", + output: null, + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 56 + }] + }, { code: "const hasProperty = Object.prototype.hasOwnProperty.call(object, property);", output: "const hasProperty = Object.hasOwn(object, property);", From 828d9d9c30315c6eb84022a30a265d6d3b9b2366 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Tue, 7 Dec 2021 08:03:04 +0530 Subject: [PATCH 21/28] fix: refactor logic and avoid false positives --- lib/rules/prefer-object-has-own.js | 32 +++++++++++++++++------- tests/lib/rules/prefer-object-has-own.js | 3 +++ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 353e3d66d67..584c0815e62 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -22,18 +22,32 @@ function hasLeftHandObject(node) { return false; } - /* - * Object.hasOwnProperty.call(obj, prop) or ({}).hasOwnProperty.call(obj, prop) - `true` - * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty - */ - if (node.object.name === "Object" || (node.object.type === "ObjectExpression" && node.object.properties.length === 0)) { - return true; + if (!node.object.object) { + return false; } - const propertyName = astUtils.getStaticPropertyName(node.object); + const objectPropertyName = astUtils.getStaticPropertyName(node.object); + + if (objectPropertyName === "hasOwnProperty") { + const childObjectPropertyName = objectPropertyName && astUtils.getStaticPropertyName(node.object.object); + let objectNodeToCheck = node.object.object; + + if (childObjectPropertyName === "prototype") { + objectNodeToCheck = node.object.object.object; + + // for case - `({}).prototype.hasOwnProperty.call(a, b)` + if (objectNodeToCheck.type === "ObjectExpression") { + return false; + } + } - if (propertyName === "hasOwnProperty" || propertyName === "prototype") { - return hasLeftHandObject(node.object); + /* + * Object.hasOwnProperty.call(obj, prop) or ({}).hasOwnProperty.call(obj, prop) - `true` + * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty + */ + if (objectNodeToCheck.name === "Object" || (objectNodeToCheck.type === "ObjectExpression" && objectNodeToCheck.properties.length === 0)) { + return true; + } } return false; diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 8aef07f0ba4..86af5fcd915 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -55,6 +55,8 @@ ruleTester.run("prefer-object-has-own", rule, { "Object.prototype.foo.call(obj, prop)", "Object.prototype.hasOwnProperty.foo(obj, prop)", "Object.prototype.hasOwnProperty.call.foo(obj, prop)", + "Object.prototype.prototype.hasOwnProperty.call(a, b);", + "Object.hasOwnProperty.prototype.hasOwnProperty.call(a, b);", "Object.prototype[hasOwnProperty].call(obj, prop)", "Object.prototype.hasOwnProperty[call](obj, prop)", "class C { #hasOwnProperty; foo() { Object.prototype.#hasOwnProperty.call(obj, prop) } }", @@ -68,6 +70,7 @@ ruleTester.run("prefer-object-has-own", rule, { "({}.hasOwnProperty(prop))", "({}.hasOwnProperty(obj, prop))", "({}.hasOwnProperty.call)", + "({}).prototype.hasOwnProperty.call(a, b);", "({}.foo.call(obj, prop))", "({}.hasOwnProperty.foo(obj, prop))", "({}[hasOwnProperty].call(obj, prop))", From 198166b8b98dea8817b39c4bcabb4cb4ef689c0d Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Tue, 7 Dec 2021 08:17:47 +0530 Subject: [PATCH 22/28] refactor: code --- lib/rules/prefer-object-has-own.js | 36 ++++++++++++------------------ 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 584c0815e62..7c251d1d6db 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -22,32 +22,24 @@ function hasLeftHandObject(node) { return false; } - if (!node.object.object) { - return false; - } - const objectPropertyName = astUtils.getStaticPropertyName(node.object); + let objectNodeToCheck = node.object; - if (objectPropertyName === "hasOwnProperty") { - const childObjectPropertyName = objectPropertyName && astUtils.getStaticPropertyName(node.object.object); - let objectNodeToCheck = node.object.object; - - if (childObjectPropertyName === "prototype") { - objectNodeToCheck = node.object.object.object; + if (objectPropertyName === "prototype") { + objectNodeToCheck = node.object.object; - // for case - `({}).prototype.hasOwnProperty.call(a, b)` - if (objectNodeToCheck.type === "ObjectExpression") { - return false; - } + // for case - `({}).prototype.hasOwnProperty.call(a, b)` + if (objectNodeToCheck.type === "ObjectExpression") { + return false; } + } - /* - * Object.hasOwnProperty.call(obj, prop) or ({}).hasOwnProperty.call(obj, prop) - `true` - * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty - */ - if (objectNodeToCheck.name === "Object" || (objectNodeToCheck.type === "ObjectExpression" && objectNodeToCheck.properties.length === 0)) { - return true; - } + /* + * Object.hasOwnProperty.call(obj, prop) or ({}).hasOwnProperty.call(obj, prop) - `true` + * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty + */ + if (objectNodeToCheck.name === "Object" || (objectNodeToCheck.type === "ObjectExpression" && objectNodeToCheck.properties.length === 0)) { + return true; } return false; @@ -78,7 +70,7 @@ module.exports = { CallExpression(node) { const calleePropertyName = astUtils.getStaticPropertyName(node.callee); const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); - const isObject = hasLeftHandObject(node.callee); + const isObject = node.callee.object && hasLeftHandObject(node.callee.object); // check `Object` scope const scope = context.getScope(); From 427c90d1e0b3e06c12fe3f97558aa82b7dc625c2 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Tue, 7 Dec 2021 19:31:37 +0530 Subject: [PATCH 23/28] docs: apply latest feedback --- docs/rules/prefer-object-has-own.md | 14 ++++++++++---- lib/rules/prefer-object-has-own.js | 8 ++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 54e7e843c22..fe28e0fc7be 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -1,6 +1,6 @@ -# Prefer `Object.hasOwn(…)` over `Object.prototype.hasOwnProperty.call(…)` (prefer-object-has-own) +# Prefer `Object.hasOwn()` over `Object.prototype.hasOwnProperty.call()` (prefer-object-has-own) -`Object.hasOwn(…)` is more accessible than `Object.prototype.hasOwnProperty.call(…)`. +`Object.hasOwn()` is more accessible than `Object.prototype.hasOwnProperty.call()`. It is recommended over `Object#hasOwnProperty()` because it works for objects created using `Object.create(null)` and with objects that have overridden the inherited `hasOwnProperty()` method. @@ -29,7 +29,9 @@ Examples of **incorrect** code for this rule: Object.prototype.hasOwnProperty.call(obj, "a"); -({}).hasOwnProperty(obj, "a"); +Object.hasOwnProperty.call(obj, "a"); + +({}).hasOwnProperty.call(obj, "a"); const hasProperty = Object.prototype.hasOwnProperty.call(object, property); ``` @@ -44,6 +46,10 @@ Object.hasOwn(obj, "a"); const hasProperty = Object.hasOwn(object, property); ``` -## Related Material +## When Not To Use It + +This rule should not be used unless ES2022 is supported in your codebase. + +## Further Reading * [Object.hasOwn()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index 7c251d1d6db..feb6fdfefa2 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -1,5 +1,5 @@ /** - * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty + * @fileoverview Prefers Object.hasOwn() instead of Object.prototype.hasOwnProperty.call() * @author Nitin Kumar * @author Gautam Arora */ @@ -55,13 +55,13 @@ module.exports = { type: "suggestion", docs: { description: - "disallow use of Object.prototype.hasOwnProperty.call(…) and prefer use of Object.hasOwn(…)", + "disallow use of `Object.prototype.hasOwnProperty.call())` and prefer use of `Object.hasOwn()`", recommended: false, url: "https://eslint.org/docs/rules/prefer-object-has-own" }, schema: [], messages: { - useHasOwn: "Prefer using Object.hasOwn(…) over Object.prototype.hasOwnProperty.call(…)." + useHasOwn: "Use 'Object.hasOwn()' instead of 'Object.prototype.hasOwnProperty.call()'." }, fixable: "code" }, @@ -86,7 +86,7 @@ module.exports = { node, messageId: "useHasOwn", fix(fixer) { - const sourceCode = context.getSourceCode(node); + const sourceCode = context.getSourceCode(); if (sourceCode.getCommentsInside(node.callee).length > 0) { return null; From 1c84057744bbf9c7803190b1c7d092baa2d921e3 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Wed, 8 Dec 2021 06:37:36 +0530 Subject: [PATCH 24/28] refactor: apply the latest suggestions --- lib/rules/prefer-object-has-own.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index feb6fdfefa2..f2e1b60f283 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -13,7 +13,7 @@ const astUtils = require("./utils/ast-utils"); /** - * Checks to see if a property name object exists in the subtree recursively. + * Checks to see if a property name 'Object' or an empty 'ObjectExpression' ({}) exists in the subtree. * @param {ASTnode} node to evalutate. * @returns {boolean} `true` if object property exists, `false` otherwise. */ @@ -22,23 +22,18 @@ function hasLeftHandObject(node) { return false; } - const objectPropertyName = astUtils.getStaticPropertyName(node.object); - let objectNodeToCheck = node.object; - - if (objectPropertyName === "prototype") { - objectNodeToCheck = node.object.object; - - // for case - `({}).prototype.hasOwnProperty.call(a, b)` - if (objectNodeToCheck.type === "ObjectExpression") { - return false; - } - } - /* - * Object.hasOwnProperty.call(obj, prop) or ({}).hasOwnProperty.call(obj, prop) - `true` + * ({}).hasOwnProperty.call(obj, prop) - `true` * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty */ - if (objectNodeToCheck.name === "Object" || (objectNodeToCheck.type === "ObjectExpression" && objectNodeToCheck.properties.length === 0)) { + if (node.object.type === "ObjectExpression" && node.object.properties.length === 0) { + return true; + } + + const objectPropertyName = astUtils.getStaticPropertyName(node.object); + const objectNodeToCheck = objectPropertyName === "prototype" ? node.object.object : node.object; + + if (objectNodeToCheck.name === "Object") { return true; } @@ -68,6 +63,10 @@ module.exports = { create(context) { return { CallExpression(node) { + if (!(node.callee.type === "MemberExpression" && node.callee.object.type === "MemberExpression")) { + return; + } + const calleePropertyName = astUtils.getStaticPropertyName(node.callee); const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); const isObject = node.callee.object && hasLeftHandObject(node.callee.object); From e36503a7f708d617edc399df2582fc89162ad798 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Wed, 8 Dec 2021 18:02:02 +0530 Subject: [PATCH 25/28] refactor: apply the latest feedback --- lib/rules/prefer-object-has-own.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/rules/prefer-object-has-own.js b/lib/rules/prefer-object-has-own.js index f2e1b60f283..dd21e95d47e 100644 --- a/lib/rules/prefer-object-has-own.js +++ b/lib/rules/prefer-object-has-own.js @@ -13,14 +13,11 @@ const astUtils = require("./utils/ast-utils"); /** - * Checks to see if a property name 'Object' or an empty 'ObjectExpression' ({}) exists in the subtree. - * @param {ASTnode} node to evalutate. - * @returns {boolean} `true` if object property exists, `false` otherwise. + * Checks if the given node is considered to be an access to a property of `Object.prototype`. + * @param {ASTNode} node `MemberExpression` node to evaluate. + * @returns {boolean} `true` if `node.object` is `Object`, `Object.prototype`, or `{}` (empty 'ObjectExpression' node). */ function hasLeftHandObject(node) { - if (!node.object) { - return false; - } /* * ({}).hasOwnProperty.call(obj, prop) - `true` @@ -30,10 +27,9 @@ function hasLeftHandObject(node) { return true; } - const objectPropertyName = astUtils.getStaticPropertyName(node.object); - const objectNodeToCheck = objectPropertyName === "prototype" ? node.object.object : node.object; + const objectNodeToCheck = node.object.type === "MemberExpression" && astUtils.getStaticPropertyName(node.object) === "prototype" ? node.object.object : node.object; - if (objectNodeToCheck.name === "Object") { + if (objectNodeToCheck.type === "Identifier" && objectNodeToCheck.name === "Object") { return true; } @@ -50,7 +46,7 @@ module.exports = { type: "suggestion", docs: { description: - "disallow use of `Object.prototype.hasOwnProperty.call())` and prefer use of `Object.hasOwn()`", + "disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()`", recommended: false, url: "https://eslint.org/docs/rules/prefer-object-has-own" }, @@ -69,7 +65,7 @@ module.exports = { const calleePropertyName = astUtils.getStaticPropertyName(node.callee); const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object); - const isObject = node.callee.object && hasLeftHandObject(node.callee.object); + const isObject = hasLeftHandObject(node.callee.object); // check `Object` scope const scope = context.getScope(); From a5024398fe7dec76b61d628ae943318483bb952e Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Wed, 8 Dec 2021 18:08:06 +0530 Subject: [PATCH 26/28] test: add more cases --- tests/lib/rules/prefer-object-has-own.js | 69 +++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/tests/lib/rules/prefer-object-has-own.js b/tests/lib/rules/prefer-object-has-own.js index 86af5fcd915..a34cf774555 100644 --- a/tests/lib/rules/prefer-object-has-own.js +++ b/tests/lib/rules/prefer-object-has-own.js @@ -1,5 +1,5 @@ /** - * @fileoverview Prefers Object.hasOwn instead of Object.prototype.hasOwnProperty + * @fileoverview Tests for prefer-object-has-own rule. * @author Nitin Kumar * @author Gautam Arora */ @@ -80,6 +80,7 @@ ruleTester.run("prefer-object-has-own", rule, { "class C { #hasOwnProperty; foo() { ({}.#hasOwnProperty.call(obj, prop)) } }", "class C { #call; foo() { ({}.hasOwnProperty.#call(obj, prop)) } }", "({ foo }.hasOwnProperty.call(obj, prop))", // object literal should be empty + "(Object) => ({}).hasOwnProperty.call(obj, prop)", // Object is shadowed, so Object.hasOwn cannot be used here ` let obj = {}; Object.hasOwn(obj,""); @@ -277,6 +278,72 @@ ruleTester.run("prefer-object-has-own", rule, { endLine: 1, endColumn: 63 }] + }, + { + code: "Object['prototype']['hasOwnProperty']['call'](object, property);", + output: "Object.hasOwn(object, property);", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 64 + }] + }, + { + code: "Object[`prototype`][`hasOwnProperty`][`call`](object, property);", + output: "Object.hasOwn(object, property);", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 64 + }] + }, + { + code: "Object['hasOwnProperty']['call'](object, property);", + output: "Object.hasOwn(object, property);", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 51 + }] + }, + { + code: "Object[`hasOwnProperty`][`call`](object, property);", + output: "Object.hasOwn(object, property);", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 51 + }] + }, + { + code: "({})['hasOwnProperty']['call'](object, property);", + output: "Object.hasOwn(object, property);", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 49 + }] + }, + { + code: "({})[`hasOwnProperty`][`call`](object, property);", + output: "Object.hasOwn(object, property);", + errors: [{ + messageId: "useHasOwn", + line: 1, + column: 1, + endLine: 1, + endColumn: 49 + }] } ] }); From 46ba42e17b8c2899592da12b74321cd407977685 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 9 Dec 2021 12:25:22 +0530 Subject: [PATCH 27/28] docs: update --- docs/rules/prefer-object-has-own.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index fe28e0fc7be..027a89b3791 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -1,23 +1,21 @@ # Prefer `Object.hasOwn()` over `Object.prototype.hasOwnProperty.call()` (prefer-object-has-own) -`Object.hasOwn()` is more accessible than `Object.prototype.hasOwnProperty.call()`. - -It is recommended over `Object#hasOwnProperty()` because it works for objects created using `Object.create(null)` and with objects that have overridden the inherited `hasOwnProperty()` method. +It is very common to write code like: ```js -const foo = { - hasOwnProperty: function() { - return false; - }, - bar: 'Hello World' -}; +if (Object.prototype.hasOwnProperty.call(object, "foo")) { + console.log("has property foo"); +} +``` -console.log(Object.hasOwn(foo, 'bar')); // true - re-implementation of hasOwnProperty() does not affect Object +This is a common practice because methods on Object.prototype can sometimes be unavailable or redefined (see the [no-prototype-bultins](no-prototype-builtins.md) rule). -const bar = Object.create(null); -bar.prop = 'exists'; +Introduced in ES2022, Object.hasOwn() is a shorter alternative to Object.prototype.hasOwnProperty.call(): -console.log(Object.hasOwn(bar, 'prop')); // true - works irrespective of how the object is created. +```js +if (Object.hasOwn(object, "foo")) { + console.log("has property foo") +} ``` ## Rule Details From 18040d49c589ee9eab126e28d0f88f3799909042 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Thu, 9 Dec 2021 17:52:53 +0530 Subject: [PATCH 28/28] docs: apply suggestions from code review Co-authored-by: Milos Djermanovic --- docs/rules/prefer-object-has-own.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md index 027a89b3791..4c90078c063 100644 --- a/docs/rules/prefer-object-has-own.md +++ b/docs/rules/prefer-object-has-own.md @@ -8,9 +8,9 @@ if (Object.prototype.hasOwnProperty.call(object, "foo")) { } ``` -This is a common practice because methods on Object.prototype can sometimes be unavailable or redefined (see the [no-prototype-bultins](no-prototype-builtins.md) rule). +This is a common practice because methods on `Object.prototype` can sometimes be unavailable or redefined (see the [no-prototype-builtins](no-prototype-builtins.md) rule). -Introduced in ES2022, Object.hasOwn() is a shorter alternative to Object.prototype.hasOwnProperty.call(): +Introduced in ES2022, `Object.hasOwn()` is a shorter alternative to `Object.prototype.hasOwnProperty.call()`: ```js if (Object.hasOwn(object, "foo")) {