Repository: sindresorhus/eslint-plugin-unicorn (JavaScript)

Issue:
Rule proposal: `prefer-object-has-own`
<!-- 🍩 Please don't ignore this template -->

<!-- 1️⃣ Explain here why this rule would be beneficial -->

Prefer Stage 3 proposal [Object.hasOwn()](https://github.com/tc39/proposal-accessible-object-hasownproperty) over `Object.prototype.hasOwnProperty.call()`

## Fail

<!-- 2️⃣ Specify an example of code that should be detected -->

```js
const hasProperty = Object.prototype.hasOwnProperty.call(object, property);
```

```js
const hasProperty = {}.hasOwnProperty.call(object, property);
```

## Pass

<!-- 3️⃣ Specify an example of code that would be accepted in its place -->

```js
const hasProperty = Object.hasOwn(object, property);
```



Discussion:
Should we wait until this proposal reach to stage 4? That's [what ESLint does](https://github.com/eslint/eslint#what-about-experimental-features).
@yangmingshan No, we implement things when we think they're likely to happened. That usually means stage 3 and up. ESLint is a bit too conservative. However, we disable these rules by default in the recommended preset.
FYI to anyone landing here: This has been added to the core:

- https://eslint.org/docs/latest/rules/prefer-object-has-own



Patch:
diff --git a/docs/rules/prefer-object-has-own.md b/docs/rules/prefer-object-has-own.md
new file mode 100644
index 0000000000..3a1c725b69
--- /dev/null
+++ b/docs/rules/prefer-object-has-own.md
@@ -0,0 +1,58 @@
+# Prefer `Object.hasOwn(…)` over `Object.prototype.hasOwnProperty.call(…)`
+
+[`Object.hasOwn(…)`](https://github.com/tc39/proposal-accessible-object-hasownproperty) is more accessible than `Object.prototype.hasOwnProperty.call(…)`.
+
+This rule is fixable.
+
+## Fail
+
+```js
+const hasProperty = Object.prototype.hasOwnProperty.call(object, property);
+```
+
+```js
+const hasProperty = {}.hasOwnProperty.call(object, property);
+```
+
+```js
+const hasProperty = lodash.has(object, property);
+```
+
+## Pass
+
+```js
+const hasProperty = Object.hasOwn(object, property);
+```
+
+## Options
+
+Type: `object`
+
+### functions
+
+Type: `string[]`
+
+You can also check custom functions that indicating the object has the specified property as its own property.
+
+`_.has()`, `lodash.has()`, and `underscore.has()` are checked by default.
+
+Example:
+
+```js
+{
+	'unicorn/prefer-object-has-own': [
+		'error',
+		{
+			functions: [
+				'has',
+				'utils.has',
+			]
+		}
+	]
+}
+```
+
+```js
+// eslint unicorn/prefer-object-has-own: ["error", {"functions": ["utils.has"]}]
+const hasProperty = utils.has(object, property); // Fails
+```
diff --git a/index.js b/index.js
index e24a1df90a..479edd9cff 100644
--- a/index.js
+++ b/index.js
@@ -101,6 +101,8 @@ module.exports = {
 				'unicorn/prefer-negative-index': 'error',
 				'unicorn/prefer-node-protocol': 'error',
 				'unicorn/prefer-number-properties': 'error',
+				// TODO: Enable this by default when targeting Node.js support `Object.hasOwn`.
+				'unicorn/prefer-object-has-own': 'off',
 				'unicorn/prefer-optional-catch-binding': 'error',
 				'unicorn/prefer-prototype-methods': 'error',
 				'unicorn/prefer-query-selector': 'error',
diff --git a/readme.md b/readme.md
index ae9fddaeec..d6eeda3baf 100644
--- a/readme.md
+++ b/readme.md
@@ -97,6 +97,7 @@ Configure it in `package.json`.
 			"unicorn/prefer-negative-index": "error",
 			"unicorn/prefer-node-protocol": "error",
 			"unicorn/prefer-number-properties": "error",
+			"unicorn/prefer-object-has-own": "off",
 			"unicorn/prefer-optional-catch-binding": "error",
 			"unicorn/prefer-prototype-methods": "error",
 			"unicorn/prefer-query-selector": "error",
@@ -196,6 +197,7 @@ Each rule has emojis denoting:
 | [prefer-negative-index](docs/rules/prefer-negative-index.md) | Prefer negative index over `.length - index` for `{String,Array,TypedArray}#slice()`, `Array#splice()` and `Array#at()`. | ✅ | 🔧 |  |
 | [prefer-node-protocol](docs/rules/prefer-node-protocol.md) | Prefer using the `node:` protocol when importing Node.js builtin modules. | ✅ | 🔧 |  |
 | [prefer-number-properties](docs/rules/prefer-number-properties.md) | Prefer `Number` static properties over global ones. | ✅ | 🔧 | 💡 |
+| [prefer-object-has-own](docs/rules/prefer-object-has-own.md) | Prefer `Object.hasOwn(…)` over `Object.prototype.hasOwnProperty.call(…)`. |  | 🔧 |  |
 | [prefer-optional-catch-binding](docs/rules/prefer-optional-catch-binding.md) | Prefer omitting the `catch` binding parameter. | ✅ | 🔧 |  |
 | [prefer-prototype-methods](docs/rules/prefer-prototype-methods.md) | Prefer borrowing methods from the prototype instead of the instance. | ✅ | 🔧 |  |
 | [prefer-query-selector](docs/rules/prefer-query-selector.md) | Prefer `.querySelector()` over `.getElementById()`, `.querySelectorAll()` over `.getElementsByClassName()` and `.getElementsByTagName()`. | ✅ | 🔧 |  |
diff --git a/rules/prefer-object-has-own.js b/rules/prefer-object-has-own.js
new file mode 100644
index 0000000000..42b974c90a
--- /dev/null
+++ b/rules/prefer-object-has-own.js
@@ -0,0 +1,96 @@
+'use strict';
+const getDocumentationUrl = require('./utils/get-documentation-url');
+const {isNodeMatches, isNodeMatchesNameOrPath} = require('./utils/is-node-matches');
+const {
+	objectPrototypeMethodSelector,
+	methodCallSelector,
+	callExpressionSelector
+} = require('./selectors');
+
+const MESSAGE_ID = 'prefer-object-has-own';
+const messages = {
+	[MESSAGE_ID]: 'Use `Object.hasOwn(…)` instead of `{{description}}(…)`.'
+};
+
+const objectPrototypeHasOwnProperty = [
+	methodCallSelector({name: 'call', length: 2}),
+	' > ',
+	objectPrototypeMethodSelector({
+		path: 'object',
+		name: 'hasOwnProperty'
+	}),
+	'.callee'
+].join('');
+
+const lodashHasFunctions = [
+	'_.has',
+	'lodash.has',
+	'underscore.has'
+];
+
+/** @param {import('eslint').Rule.RuleContext} context */
+const create = context => {
+	const {functions: configFunctions} = {
+		functions: [],
+		...context.options[0]
+	};
+	const functions = [...configFunctions, ...lodashHasFunctions];
+
+	return Object.fromEntries(
+		[
+			{
+				selector: objectPrototypeHasOwnProperty,
+				description: 'Object.prototype.hasOwnProperty.call'
+			},
+			{
+				selector: `${callExpressionSelector({length: 2})} > .callee`,
+				test: node => isNodeMatches(node, functions),
+				description: node => functions.find(nameOrPath => isNodeMatchesNameOrPath(node, nameOrPath)).trim()
+			}
+		].map(({selector, test, description}) => [
+			selector,
+			node => {
+				if (test && !test(node)) {
+					return;
+				}
+
+				context.report({
+					node,
+					messageId: MESSAGE_ID,
+					data: {
+						description: typeof description === 'string' ? description : description(node)
+					},
+					/** @param {import('eslint').Rule.RuleFixer} fixer */
+					fix: fixer => fixer.replaceText(node, 'Object.hasOwn')
+				});
+			}
+		])
+	);
+};
+
+const schema = [
+	{
+		type: 'object',
+		properties: {
+			functions: {
+				type: 'array',
+				uniqueItems: true
+			}
+		},
+		additionalProperties: false
+	}
+];
+
+module.exports = {
+	create,
+	meta: {
+		type: 'suggestion',
+		docs: {
+			description: 'Prefer `Object.hasOwn(…)` over `Object.prototype.hasOwnProperty.call(…)`.',
+			url: getDocumentationUrl(__filename)
+		},
+		fixable: 'code',
+		schema,
+		messages
+	}
+};
diff --git a/rules/selectors/array-prototype-method-selector.js b/rules/selectors/array-prototype-method-selector.js
deleted file mode 100644
index 57fb4472eb..0000000000
--- a/rules/selectors/array-prototype-method-selector.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict';
-const matches = require('./matches-any');
-const memberExpressionSelector = require('./member-expression-selector');
-const emptyArraySelector = require('./empty-array-selector');
-
-// `[].method` or `Array.prototype.method`
-function arrayPrototypeMethodSelector(options) {
-	const {
-		name,
-		names,
-		path
-	} = {
-		path: '',
-		name: '',
-		...options
-	};
-
-	const objectPath = path ? `${path}.object` : 'object';
-
-	return [
-		memberExpressionSelector({
-			path,
-			name,
-			names
-		}),
-		matches([
-			emptyArraySelector(objectPath),
-			memberExpressionSelector({path: objectPath, name: 'prototype', object: 'Array'})
-		])
-	].join('');
-}
-
-module.exports = arrayPrototypeMethodSelector;
diff --git a/rules/selectors/empty-object-selector.js b/rules/selectors/empty-object-selector.js
new file mode 100644
index 0000000000..f4cd94e046
--- /dev/null
+++ b/rules/selectors/empty-object-selector.js
@@ -0,0 +1,11 @@
+'use strict';
+
+function emptyObjectSelector(path) {
+	const prefix = `${path}.`;
+	return [
+		`[${prefix}type="ObjectExpression"]`,
+		`[${prefix}properties.length=0]`
+	].join('');
+}
+
+module.exports = emptyObjectSelector;
diff --git a/rules/selectors/index.js b/rules/selectors/index.js
index bffd88cc20..0b96e77a23 100644
--- a/rules/selectors/index.js
+++ b/rules/selectors/index.js
@@ -5,7 +5,8 @@ module.exports = {
 	matches: require('./matches-any'),
 	not: require('./negation'),
 
-	arrayPrototypeMethodSelector: require('./array-prototype-method-selector'),
+	arrayPrototypeMethodSelector: require('./prototype-method-selector').arrayPrototypeMethodSelector,
+	objectPrototypeMethodSelector: require('./prototype-method-selector').objectPrototypeMethodSelector,
 	emptyArraySelector: require('./empty-array-selector'),
 	memberExpressionSelector: require('./member-expression-selector'),
 	methodCallSelector: require('./method-call-selector'),
diff --git a/rules/selectors/prototype-method-selector.js b/rules/selectors/prototype-method-selector.js
new file mode 100644
index 0000000000..94ec6ca704
--- /dev/null
+++ b/rules/selectors/prototype-method-selector.js
@@ -0,0 +1,53 @@
+'use strict';
+const matches = require('./matches-any');
+const memberExpressionSelector = require('./member-expression-selector');
+const emptyArraySelector = require('./empty-array-selector');
+const emptyObjectSelector = require('./empty-object-selector');
+
+function prototypeMethodSelector(options) {
+	const {
+		object,
+		name,
+		names,
+		path
+	} = {
+		path: '',
+		name: '',
+		...options
+	};
+
+	const objectPath = path ? `${path}.object` : 'object';
+
+	const prototypeSelectors = [
+		memberExpressionSelector({path: objectPath, name: 'prototype', object})
+	];
+
+	switch (object) {
+		case 'Array':
+			// `[].method` or `Array.prototype.method`
+			prototypeSelectors.push(emptyArraySelector(objectPath));
+			break;
+		case 'Object':
+			// `{}.method` or `Object.prototype.method`
+			prototypeSelectors.push(emptyObjectSelector(objectPath));
+			break;
+		// No default
+	}
+
+	return [
+		memberExpressionSelector({
+			path,
+			name,
+			names
+		}),
+		matches(prototypeSelectors)
+	].join('');
+}
+
+const arrayPrototypeMethodSelector = options => prototypeMethodSelector({...options, object: 'Array'});
+const objectPrototypeMethodSelector = options => prototypeMethodSelector({...options, object: 'Object'});
+
+module.exports = {
+	arrayPrototypeMethodSelector,
+	objectPrototypeMethodSelector
+};


Test patch:
diff --git a/test/prefer-object-has-own.mjs b/test/prefer-object-has-own.mjs
new file mode 100644
index 0000000000..004f688b4b
--- /dev/null
+++ b/test/prefer-object-has-own.mjs
@@ -0,0 +1,93 @@
+import {getTester} from './utils/test.mjs';
+
+const {test} = getTester(import.meta);
+
+test.snapshot({
+	valid: [
+		'const hasProperty = Object.hasOwn(object, property);',
+
+		// CallExpression
+		'Object.prototype.hasOwnProperty.call',
+		'({}).hasOwnProperty.call',
+		'foo.call(Object.prototype.hasOwnProperty, Object.prototype.hasOwnProperty.call)',
+
+		// Arguments
+		'Object.prototype.hasOwnProperty.call(object)',
+		'Object.prototype.hasOwnProperty.call()',
+		'Object.prototype.hasOwnProperty.call(object, property, extraArgument)',
+		'Object.prototype.hasOwnProperty.call(...[object, property])',
+		'({}).hasOwnProperty.call(object)',
+		'({}).hasOwnProperty.call()',
+		'({}).hasOwnProperty.call(object, property, extraArgument)',
+		'({}).hasOwnProperty.call(...[object, property])',
+
+		// Optional
+		'Object.prototype.hasOwnProperty.call?.(object, property)',
+		'Object.prototype.hasOwnProperty?.call(object, property)',
+		'Object.prototype?.hasOwnProperty.call(object, property)',
+		'Object?.prototype.hasOwnProperty.call(object, property)',
+		'({}).hasOwnProperty.call?.(object, property)',
+		'({}).hasOwnProperty?.call(object, property)',
+		'({})?.hasOwnProperty.call(object, property)',
+
+		// Computed
+		'Object.prototype.hasOwnProperty[call](object, property)',
+		'Object.prototype[hasOwnProperty].call(object, property)',
+		'Object[prototype].hasOwnProperty.call(object, property)',
+		'({}).hasOwnProperty[call](object, property)',
+		'({})[hasOwnProperty].call(object, property)',
+
+		// Names
+		'Object.prototype.hasOwnProperty.notCall(object, property)',
+		'Object.prototype.notHasOwnProperty.call(object, property)',
+		'Object.notPrototype.hasOwnProperty.call(object, property)',
+		'notObject.prototype.hasOwnProperty.call(object, property)',
+		'({}).hasOwnProperty.notCall(object, property)',
+		'({}).notHasOwnProperty.call(object, property)',
+
+		// Empty object
+		'({notEmpty}).hasOwnProperty.call(object, property)',
+		'([]).hasOwnProperty.call(object, property)'
+	],
+	invalid: [
+		'const hasProperty = Object.prototype.hasOwnProperty.call(object, property);',
+		'const hasProperty = Object.prototype.hasOwnProperty.call(object, property,);',
+		'const hasProperty = (( Object.prototype.hasOwnProperty.call(object, property) ));',
+		'const hasProperty = (( Object.prototype.hasOwnProperty.call ))(object, property);',
+		'const hasProperty = (( Object.prototype.hasOwnProperty )).call(object, property);',
+		'const hasProperty = (( Object.prototype )).hasOwnProperty.call(object, property);',
+		'const hasProperty = (( Object )).prototype.hasOwnProperty.call(object, property);',
+		'const hasProperty = {}.hasOwnProperty.call(object, property);',
+		'const hasProperty = (( {}.hasOwnProperty.call(object, property) ));',
+		'const hasProperty = (( {}.hasOwnProperty.call ))(object, property);',
+		'const hasProperty = (( {}.hasOwnProperty )).call(object, property);',
+		'const hasProperty = (( {} )).hasOwnProperty.call(object, property);'
+	]
+});
+
+// `functions`
+test.snapshot({
+	valid: [
+		'_.has(object)',
+		'_.has()',
+		'_.has(object, property, extraArgument)',
+		'_.has',
+		'_.has?.(object, property)',
+		'_?.has(object, property)',
+		'foo.has(object, property)',
+		'foo._.has(object, property)'
+	],
+	invalid: [
+		'_.has(object, property)',
+		'lodash.has(object, property)',
+		'underscore.has(object, property)',
+		{
+			code: '_.has(object, property)',
+			options: [{functions: ['utils.has']}]
+		},
+		{
+			code: 'utils.has(object, property)',
+			options: [{functions: ['utils.has']}]
+		}
+	]
+});
diff --git a/test/snapshots/prefer-object-has-own.mjs.md b/test/snapshots/prefer-object-has-own.mjs.md
new file mode 100644
index 0000000000..e85417910b
--- /dev/null
+++ b/test/snapshots/prefer-object-has-own.mjs.md
@@ -0,0 +1,301 @@
+# Snapshot report for `test/prefer-object-has-own.mjs`
+
+The actual snapshot is saved in `prefer-object-has-own.mjs.snap`.
+
+Generated by [AVA](https://avajs.dev).
+
+## Invalid #1
+      1 | const hasProperty = Object.prototype.hasOwnProperty.call(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = Object.prototype.hasOwnProperty.call(object, property);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #2
+      1 | const hasProperty = Object.prototype.hasOwnProperty.call(object, property,);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property,);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = Object.prototype.hasOwnProperty.call(object, property,);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #3
+      1 | const hasProperty = (( Object.prototype.hasOwnProperty.call(object, property) ));
+
+> Output
+
+    `␊
+      1 | const hasProperty = (( Object.hasOwn(object, property) ));␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( Object.prototype.hasOwnProperty.call(object, property) ));␊
+        |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #4
+      1 | const hasProperty = (( Object.prototype.hasOwnProperty.call ))(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = (( Object.hasOwn ))(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( Object.prototype.hasOwnProperty.call ))(object, property);␊
+        |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #5
+      1 | const hasProperty = (( Object.prototype.hasOwnProperty )).call(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( Object.prototype.hasOwnProperty )).call(object, property);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #6
+      1 | const hasProperty = (( Object.prototype )).hasOwnProperty.call(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( Object.prototype )).hasOwnProperty.call(object, property);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #7
+      1 | const hasProperty = (( Object )).prototype.hasOwnProperty.call(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( Object )).prototype.hasOwnProperty.call(object, property);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #8
+      1 | const hasProperty = {}.hasOwnProperty.call(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = {}.hasOwnProperty.call(object, property);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #9
+      1 | const hasProperty = (( {}.hasOwnProperty.call(object, property) ));
+
+> Output
+
+    `␊
+      1 | const hasProperty = (( Object.hasOwn(object, property) ));␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( {}.hasOwnProperty.call(object, property) ));␊
+        |                        ^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #10
+      1 | const hasProperty = (( {}.hasOwnProperty.call ))(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = (( Object.hasOwn ))(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( {}.hasOwnProperty.call ))(object, property);␊
+        |                        ^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #11
+      1 | const hasProperty = (( {}.hasOwnProperty )).call(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( {}.hasOwnProperty )).call(object, property);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #12
+      1 | const hasProperty = (( {} )).hasOwnProperty.call(object, property);
+
+> Output
+
+    `␊
+      1 | const hasProperty = Object.hasOwn(object, property);␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | const hasProperty = (( {} )).hasOwnProperty.call(object, property);␊
+        |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`Object.prototype.hasOwnProperty.call(…)\`.␊
+    `
+
+## Invalid #1
+      1 | _.has(object, property)
+
+> Output
+
+    `␊
+      1 | Object.hasOwn(object, property)␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | _.has(object, property)␊
+        | ^^^^^ Use \`Object.hasOwn(…)\` instead of \`_.has(…)\`.␊
+    `
+
+## Invalid #2
+      1 | lodash.has(object, property)
+
+> Output
+
+    `␊
+      1 | Object.hasOwn(object, property)␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | lodash.has(object, property)␊
+        | ^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`lodash.has(…)\`.␊
+    `
+
+## Invalid #3
+      1 | underscore.has(object, property)
+
+> Output
+
+    `␊
+      1 | Object.hasOwn(object, property)␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | underscore.has(object, property)␊
+        | ^^^^^^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`underscore.has(…)\`.␊
+    `
+
+## Invalid #4
+      1 | _.has(object, property)
+
+> Options
+
+    `␊
+    [␊
+      {␊
+        "functions": [␊
+          "utils.has"␊
+        ]␊
+      }␊
+    ]␊
+    `
+
+> Output
+
+    `␊
+      1 | Object.hasOwn(object, property)␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | _.has(object, property)␊
+        | ^^^^^ Use \`Object.hasOwn(…)\` instead of \`_.has(…)\`.␊
+    `
+
+## Invalid #5
+      1 | utils.has(object, property)
+
+> Options
+
+    `␊
+    [␊
+      {␊
+        "functions": [␊
+          "utils.has"␊
+        ]␊
+      }␊
+    ]␊
+    `
+
+> Output
+
+    `␊
+      1 | Object.hasOwn(object, property)␊
+    `
+
+> Error 1/1
+
+    `␊
+    > 1 | utils.has(object, property)␊
+        | ^^^^^^^^^ Use \`Object.hasOwn(…)\` instead of \`utils.has(…)\`.␊
+    `
diff --git a/test/snapshots/prefer-object-has-own.mjs.snap b/test/snapshots/prefer-object-has-own.mjs.snap
new file mode 100644
index 0000000000..86f94759ff
Binary files /dev/null and b/test/snapshots/prefer-object-has-own.mjs.snap differ