eslint prefer-object-has-own
labelled safe
Analysis Nemotron 3 Super
The rule flags calls to `Object.prototype.hasOwnProperty.call()` (or variants using `Object.hasOwnProperty.call`, object literals, etc.) and suggests replacing them with `Object.hasOwn()`.
Fix: Replace `X.hasOwnProperty.call(obj, prop)` where X is `Object.prototype`, `Object`, or `{}` with `Object.hasOwn(obj, prop)`.
11 hazards
- If `Object.hasOwn` is shadowed by a local variable in scope, the rewrite will refer to that variable instead of the global built-in, changing behavior.
- If the original code relied on `Object.prototype.hasOwnProperty.call` being a specific function object (e.g., for equality checks or `typeof`), replacing it with `Object.hasOwn` may break such checks.
- If the `obj` or `prop` expressions have side effects, the rewrite preserves their evaluation order only if the autotool keeps the same argument order—which it does—but any tool that reorders arguments would introduce a hazard; however, the rule’s fix does not reorder, so this is not a hazard of the fix itself.
- If `Object` is rebound (e.g., `let Object = null;`), then `Object.hasOwn` will throw or behave incorrectly, whereas the original `Object.prototype.hasOwnProperty.call` might still work if `Object.prototype` is unaffected—but this is a preexisting issue, not introduced by the fix.
- If the code is in a context where `Object.hasOwn` is not supported (e.g., older engines), the rewrite will cause a runtime error, while the original code would have worked—this is a versioning hazard, but the rule should only be used where ES2022+ is supported.
- If the call is part of a larger expression and the autofix removes parentheses implicitly relied on for grouping, it could change precedence—but the fix replaces a full CallExpression with another, preserving expression structure, so this is unlikely.
- If the original code had trailing comments on the same line after the semicolon, some autofix implementations might incorrectly discard them if they replace the whole statement without preserving trailing comments.
- If the original code used a computed property access like `obj[expr]` and the autofix does not preserve the exact spacing or formatting, it could affect readability but not behavior—this is not a behavioral hazard.
- If the `obj` expression is `undefined` or `null`, both `Object.prototype.hasOwnProperty.call` and `Object.hasOwn` throw the same TypeError, so no behavioral change occurs.
- If the original code was written as `(Object.prototype.hasOwnProperty).call(obj, prop)` and the rule somehow misfires, but the rule does not flag this form, so it’s not relevant.
- If the codebase uses a polyfill for `Object.hasOwn` that behaves differently from the native version (e.g., treats `-0` and `0` differently), then the rewrite might change behavior relative to expectations—but this depends on the polyfill, not the fix itself.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Side-effect preservation check | If arguments have side effects, the fix must preserve evaluation order. |
| r1-p002 | fix ok | Strict vs sloppy mode interaction | In strict mode, hasOwnProperty on null/undefined throws; fix behaves same but must not alter this-binding. |
| r1-p003 | fix ok | Async/await ordering with generator yield | Fix must not interfere with await/yield evaluation order in async generators. |
| r1-p004 | fix ok prediction off | ES2022+ versioning hazard | Rewrite fails in pre-ES2022 environments where Object.hasOwn is missing. |
| r1-p005 | fix ok | Label/break/finally control flow | Fix inside labeled block must not break break/continue/finally semantics. |
| r1-p006 | fix ok | Regex lastIndex interaction with global flag | Arguments involving regex.lastIndex with /g must retain evaluation order. |
| r1-p007 | fix ok | Trailing comment preservation | Autofix may discard trailing comments on same line after statement. |
| r1-p008 | fix ok | Class fields and static blocks interaction | Fix in static block must not interfere with class field initialization or super. |
| r2-p001 | fix ok | Null/undefined distinction with optional chaining short-circuit | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
| r2-p002 | fix ok | Null/undefined throws same error in both versions | Strict vs sloppy mode interaction (In strict mode, hasOwnProperty on null/undefined throws; fix behaves same but mu) |
| r2-p003 | fix ok | Parentheses reliance in larger expression (edge case testing precedence) | If the call is part of a larger expression and the autofix removes parentheses implicitly relied on for grouping, it could change precedence—but the fix replaces a full CallExpression with another, pr |
| r2-p004 | fix ok | Function declarations inside blocks, switch cases, labeled statements | function declarations inside blocks, switch cases, labeled statements |
| r2-p005 | fix ok | Var hoisting and re-declaration inside loops or after use | var hoisting and re-declaration inside loops or after use |
| r2-p006 | fix ok | ES2022+ versioning hazard: Object.hasOwn missing in older env | If the code is in a context where `Object.hasOwn` is not supported (e.g., older engines), the rewrite will cause a runtime error, while the original code would have worked—this is a versioning hazard, |
| r2-p007 | fix ok | Trailing comment preservation on same line after statement | If the original code had trailing comments on the same line after the semicolon, some autofix implementations might incorrectly discard them if they replace the whole statement without preserving trai |
| r2-p008 | fix ok | Getters, setters and Object.defineProperty side effects | getters, setters and Object.defineProperty side effects |
Sources
r1-p001 · Side-effect preservation check
1let a = 0, b = 0;
2function next() { return ++a; }
3function val() { return ++b; }
4console.log(Object.prototype.hasOwnProperty.call({ [val()]: 'x' }, next()) ? 'yes' : 'no');
5console.log(`a=${a} b=${b}`);
--- orig.js 2026-10-22 14:28:30.490417742 -0400 +++ fixed.js 2026-10-22 14:28:34.966379683 -0400 @@ -1,5 +1,5 @@ let a = 0, b = 0; function next() { return ++a; } function val() { return ++b; } -console.log(Object.prototype.hasOwnProperty.call({ [val()]: 'x' }, next()) ? 'yes' : 'no'); +console.log(Object.hasOwn({ [val()]: 'x' }, next()) ? 'yes' : 'no'); console.log(`a=${a} b=${b}`);
yes a=1 b=1
r1-p002 · Strict vs sloppy mode interaction
1"use strict";
2try {
3 console.log(Object.prototype.hasOwnProperty.call(null, 'x'));
4} catch (e) {
5 console.log(e.name);
6}
7try {
8 console.log(Object.hasOwn(null, 'x'));
9} catch (e) {
10 console.log(e.name);
11}
--- orig.js 2026-10-22 14:28:30.491417733 -0400 +++ fixed.js 2026-10-22 14:28:33.180394650 -0400 @@ -1,6 +1,6 @@ "use strict"; try { - console.log(Object.prototype.hasOwnProperty.call(null, 'x')); + console.log(Object.hasOwn(null, 'x')); } catch (e) { console.log(e.name); }
TypeError TypeError
r1-p003 · Async/await ordering with generator yield
1async function* gen() {
2 let obj = {};
3 obj[await Promise.resolve('key')] = 1;
4 if (Object.prototype.hasOwnProperty.call(obj, await Promise.resolve('key'))) {
5 yield 1;
6 }
7}
8gen().next().then(({value}) => console.log(value));
--- orig.js 2026-10-22 14:28:30.492417725 -0400 +++ fixed.js 2026-10-22 14:28:35.010379316 -0400 @@ -1,7 +1,7 @@ async function* gen() { let obj = {}; obj[await Promise.resolve('key')] = 1; - if (Object.prototype.hasOwnProperty.call(obj, await Promise.resolve('key'))) { + if (Object.hasOwn(obj, await Promise.resolve('key'))) { yield 1; } }
1
r1-p004 · ES2022+ versioning hazard
1let obj = { a: 1 };
2let result = typeof Object.hasOwn === 'function'
3 ? Object.hasOwn(obj, 'a')
4 : Object.prototype.hasOwnProperty.call(obj, 'a');
5console.log(result); // prints true only if polyfilled or modern
--- orig.js 2026-10-22 14:28:30.493417716 -0400 +++ fixed.js 2026-10-22 14:28:34.948379833 -0400 @@ -1,5 +1,5 @@ let obj = { a: 1 }; let result = typeof Object.hasOwn === 'function' ? Object.hasOwn(obj, 'a') - : Object.prototype.hasOwnProperty.call(obj, 'a'); + : Object.hasOwn(obj, 'a'); console.log(result); // prints true only if polyfilled or modern
true
r1-p005 · Label/break/finally control flow
1outer: {
2 let obj = { x: 1 };
3 if (Object.prototype.hasOwnProperty.call(obj, 'x')) {
4 console.log('found');
5 break outer;
6 }
7 console.log('missed');
8}
9console.log('done');
--- orig.js 2026-10-22 14:28:30.493417716 -0400 +++ fixed.js 2026-10-22 14:28:33.760389761 -0400 @@ -1,6 +1,6 @@ outer: { let obj = { x: 1 }; - if (Object.prototype.hasOwnProperty.call(obj, 'x')) { + if (Object.hasOwn(obj, 'x')) { console.log('found'); break outer; }
found done
r1-p006 · Regex lastIndex interaction with global flag
1let obj = {};
2let re = /a/g;
3re.lastIndex = 0;
4obj[re.exec('ba')] = true; // sets obj['a'] = true
5if (Object.prototype.hasOwnProperty.call(obj, re.exec('ba')[0])) {
6 console.log('has');
7} else {
8 console.log('missing');
9}
10console.log(re.lastIndex); // should be 2 if two execs ran
--- orig.js 2026-10-22 14:28:30.494417707 -0400 +++ fixed.js 2026-10-22 14:28:35.028379168 -0400 @@ -2,7 +2,7 @@ let re = /a/g; re.lastIndex = 0; obj[re.exec('ba')] = true; // sets obj['a'] = true -if (Object.prototype.hasOwnProperty.call(obj, re.exec('ba')[0])) { +if (Object.hasOwn(obj, re.exec('ba')[0])) { console.log('has'); } else { console.log('missing');
r1-p007 · Trailing comment preservation
1let obj = { prop: 42 };
2let has = Object.prototype.hasOwnProperty.call(obj, 'prop'); // keep this comment
3console.log(has); // should print true
4// Expected output: true then comment about keeping
--- orig.js 2026-10-22 14:28:34.845380691 -0400 +++ fixed.js 2026-10-22 14:28:36.894363861 -0400 @@ -1,4 +1,4 @@ let obj = { prop: 42 }; -let has = Object.prototype.hasOwnProperty.call(obj, 'prop'); // keep this comment +let has = Object.hasOwn(obj, 'prop'); // keep this comment console.log(has); // should print true // Expected output: true then comment about keeping
true
r1-p008 · Class fields and static blocks interaction
1class C {
2 static staticField;
3 static {
4 let tmp = { prop: 1 };
5 C.staticField = Object.prototype.hasOwnProperty.call(tmp, 'prop') ? 'yes' : 'no';
6 }
7 instanceField = Object.hasOwn(C.prototype, 'instanceField') ? 1 : 0;
8}
9console.log(C.staticField);
10console.log(new C().instanceField);
--- orig.js 2026-10-22 14:28:34.880380400 -0400 +++ fixed.js 2026-10-22 14:28:38.593350204 -0400 @@ -2,7 +2,7 @@ static staticField; static { let tmp = { prop: 1 }; - C.staticField = Object.prototype.hasOwnProperty.call(tmp, 'prop') ? 'yes' : 'no'; + C.staticField = Object.hasOwn(tmp, 'prop') ? 'yes' : 'no'; } instanceField = Object.hasOwn(C.prototype, 'instanceField') ? 1 : 0; }
yes 0
r2-p001 · Null/undefined distinction with optional chaining short-circuit
1let o = {a: 1}; let x;
2function getObj() { return x; }
3console.log(getObj()?.a ?? 'missing');
4if (Object.prototype.hasOwnProperty.call(getObj(), 'a')) {
5 console.log('has own');
6} else {
7 console.log('does not have own');
8}
--- orig.js 2026-10-22 14:28:44.968301252 -0400 +++ fixed.js 2026-10-22 14:28:49.334269846 -0400 @@ -1,7 +1,7 @@ let o = {a: 1}; let x; function getObj() { return x; } console.log(getObj()?.a ?? 'missing'); -if (Object.prototype.hasOwnProperty.call(getObj(), 'a')) { +if (Object.hasOwn(getObj(), 'a')) { console.log('has own'); } else { console.log('does not have own');
missing
r2-p002 · Null/undefined throws same error in both versions
1'use strict';
2try {
3 Object.prototype.hasOwnProperty.call(null, 'x');
4} catch (e) {
5 console.log(e.name + ': ' + e.message);
6}
7try {
8 Object.hasOwn(null, 'x');
9} catch (e) {
10 console.log(e.name + ': ' + e.message);
11}
--- orig.js 2026-10-22 14:28:44.969301245 -0400 +++ fixed.js 2026-10-22 14:28:49.266270321 -0400 @@ -1,6 +1,6 @@ 'use strict'; try { - Object.prototype.hasOwnProperty.call(null, 'x'); + Object.hasOwn(null, 'x'); } catch (e) { console.log(e.name + ': ' + e.message); }
TypeError: Cannot convert undefined or null to object TypeError: Cannot convert undefined or null to object
r2-p003 · Parentheses reliance in larger expression (edge case testing precedence)
1let o = {a: 1};
2let result = !Object.prototype.hasOwnProperty.call(o, 'a') && 0;
3console.log('Result:', result);
4result = !Object.hasOwn(o, 'a') && 0;
5console.log('Result after fix:', result);
--- orig.js 2026-10-22 14:28:44.970301237 -0400 +++ fixed.js 2026-10-22 14:28:48.139278270 -0400 @@ -1,5 +1,5 @@ let o = {a: 1}; -let result = !Object.prototype.hasOwnProperty.call(o, 'a') && 0; +let result = !Object.hasOwn(o, 'a') && 0; console.log('Result:', result); result = !Object.hasOwn(o, 'a') && 0; console.log('Result after fix:', result);
Result: false Result after fix: false
r2-p004 · Function declarations inside blocks, switch cases, labeled statements
1function test() {
2 let o = {x: 1};
3 switch (o.x) {
4 case 1:
5 function inner() { return Object.prototype.hasOwnProperty.call(o, 'x'); }
6 console.log(inner());
7 break;
8 }
9 label: {
10 if (Object.prototype.hasOwnProperty.call(o, 'x')) {
11 break label;
12 }
13 console.log('should not reach');
14 }
15}
16test();
--- orig.js 2026-10-22 14:28:44.970301237 -0400 +++ fixed.js 2026-10-22 14:28:49.287270174 -0400 @@ -2,12 +2,12 @@ let o = {x: 1}; switch (o.x) { case 1: - function inner() { return Object.prototype.hasOwnProperty.call(o, 'x'); } + function inner() { return Object.hasOwn(o, 'x'); } console.log(inner()); break; } label: { - if (Object.prototype.hasOwnProperty.call(o, 'x')) { + if (Object.hasOwn(o, 'x')) { break label; } console.log('should not reach');
true
r2-p005 · Var hoisting and re-declaration inside loops or after use
1for (var i = 0; i < 2; i++) {
2 var o = {a: i};
3 if (Object.prototype.hasOwnProperty.call(o, 'a')) {
4 console.log('loop', i, 'has a');
5 }
6}
7console.log('o after loop:', typeof o);
8if (Object.prototype.hasOwnProperty.call(o, 'a')) {
9 console.log('o has a');
10} else {
11 console.log('o does not have a');
12}
--- orig.js 2026-10-22 14:28:44.970301237 -0400 +++ fixed.js 2026-10-22 14:28:49.312269999 -0400 @@ -1,11 +1,11 @@ for (var i = 0; i < 2; i++) { var o = {a: i}; - if (Object.prototype.hasOwnProperty.call(o, 'a')) { + if (Object.hasOwn(o, 'a')) { console.log('loop', i, 'has a'); } } console.log('o after loop:', typeof o); -if (Object.prototype.hasOwnProperty.call(o, 'a')) { +if (Object.hasOwn(o, 'a')) { console.log('o has a'); } else { console.log('o does not have a');
loop 0 has a loop 1 has a o after loop: object o has a
r2-p006 · ES2022+ versioning hazard: Object.hasOwn missing in older env
1let o = {a: 1};
2if (typeof Object.hasOwn !== 'function') {
3 console.log('Object.hasOwn not available, simulating old engine');
4 // We still call the original to show it works
5 console.log(Object.prototype.hasOwnProperty.call(o, 'a'));
6} else {
7 console.log(Object.prototype.hasOwnProperty.call(o, 'a'));
8 console.log(Object.hasOwn(o, 'a'));
9}
--- orig.js 2026-10-22 14:28:44.971301230 -0400 +++ fixed.js 2026-10-22 14:28:47.051286053 -0400 @@ -2,8 +2,8 @@ if (typeof Object.hasOwn !== 'function') { console.log('Object.hasOwn not available, simulating old engine'); // We still call the original to show it works - console.log(Object.prototype.hasOwnProperty.call(o, 'a')); + console.log(Object.hasOwn(o, 'a')); } else { - console.log(Object.prototype.hasOwnProperty.call(o, 'a')); + console.log(Object.hasOwn(o, 'a')); console.log(Object.hasOwn(o, 'a')); }
true true
r2-p007 · Trailing comment preservation on same line after statement
1let o = {a: 1};
2if (Object.prototype.hasOwnProperty.call(o, 'a')) { // check if a exists
3 console.log('yes');
4} // end if
5console.log('done');
--- orig.js 2026-10-22 14:28:49.221270635 -0400 +++ fixed.js 2026-10-22 14:28:51.297256273 -0400 @@ -1,5 +1,5 @@ let o = {a: 1}; -if (Object.prototype.hasOwnProperty.call(o, 'a')) { // check if a exists +if (Object.hasOwn(o, 'a')) { // check if a exists console.log('yes'); } // end if console.log('done');
yes done
r2-p008 · Getters, setters and Object.defineProperty side effects
1let obj = {};
2let val = 42;
3Object.defineProperty(obj, 'prop', {
4 get() { console.log('getter called'); return val; },
5 enumerable: true,
6 configurable: true
7});
8console.log('Before check, val =', val);
9if (Object.prototype.hasOwnProperty.call(obj, 'prop')) {
10 console.log('Has own prop');
11}
12console.log('After check, val =', val);
--- orig.js 2026-10-22 14:28:49.738267023 -0400 +++ fixed.js 2026-10-22 14:28:52.357249087 -0400 @@ -6,7 +6,7 @@ configurable: true }); console.log('Before check, val =', val); -if (Object.prototype.hasOwnProperty.call(obj, 'prop')) { +if (Object.hasOwn(obj, 'prop')) { console.log('Has own prop'); } console.log('After check, val =', val);
Before check, val = 42 Has own prop After check, val = 42