fixhunt

eslint prefer-object-spread

labelled safe
Runs / 2026-10-22T18-28-53-723Z-usww-eslint-prefer-object-spreadeslint 9.39.52026-10-22 18:28 UTC8 programs × 2 round(s)21 programs, 19 fired, 19 fixed, 0 findings, 1 rejectedmodel cost $0.01

Analysis Nemotron 3 Super

The rule flags uses of Object.assign with an object literal as the first argument and suggests replacing them with object spread syntax.

Fix: Replace Object.assign calls with an object literal as the first argument by spreading the remaining arguments into an object literal expression; if only one argument (the object literal), remove Object.assign entirely and keep just the object literal.

8 hazards
  • Rewriting Object.assign({}, x) to ({ ...x }) changes behavior if x is null or undefined, as spread throws TypeError while Object.assign treats them as empty objects.
  • If the first argument in Object.assign has properties, spreading discards them (e.g., Object.assign({a:1}, {b:2}) becomes {...{b:2}} losing {a:1}), altering the result.
  • Spread syntax does not copy property descriptors (like getters/setters or non-enumerable flags), whereas Object.assign does, potentially changing object behavior.
  • In loose mode or older environments, spread syntax may be a SyntaxError, causing compilation failure where Object.assign would run.
  • If the spread argument contains getters, they are invoked during spread evaluation, potentially causing side effects not present in Object.assign which copies the getter function itself.
  • Comments attached to the Object.assign call or its arguments may be lost or misplaced during the rewrite, especially if the autofix removes the call entirely.
  • When Object.assign is used as part of a larger expression (e.g., return Object.assign({}, x)), rewriting to ({ ...x }) may alter precedence or grouping if not parenthesized correctly.
  • If the first argument is a complex expression (e.g., Object.assign(getObj(), {y:2})), the fix incorrectly spreads getObj() instead of preserving the call and merging, changing evaluation and possibly causing errors if getObj() returns null.

Programs

idresulttitlehazard
r1-p002fix okThis Binding Hazardthis binding: arrow vs function, methods extracted from objects, call/apply/bind
r1-p003fix okGetter/Setter Hazardgetters, setters and Object.defineProperty side effects
r1-p004fix okTemporal Dead Zone Hazardtemporal dead zone after code is hoisted or reordered
r1-p005fix okSparse Array Hazardsparse arrays, holes, Array(n) vs [n], array-likes
r1-p006fix okVar Hoisting Hazardvar hoisting and re-declaration inside loops or after use
r1-p007fix okNull/Undefined Hazardnull/undefined distinction, optional chaining short-circuit, nullish vs falsy
r1-p008fix okClass Fields Hazardclass fields, static blocks, private names, super calls
r2-p001fix okSpread Syntax Does Not Copy Property DescriptorsSpread syntax does not copy property descriptors (like getters/setters or non-enumerable flags), whereas Object.assign does, potentially changing object behavior.
r2-p002fix okNumeric/String Coercion with toString/valueOfnumeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt
r2-p003fix okName Collisions Across Scopes After a Movename collisions across scopes after a move (shadowing)
r2-p004fix okFunction Declarations Inside Blocks/Switch/Labeled Statementsfunction declarations inside blocks, switch cases, labeled statements
r2-p005fix okSparse Arrays, Holes, Array(n) vs [n]sparse arrays, holes, Array(n) vs [n], array-likes
r2-p006silent prediction offComplex Expression as First Argument (Evaluation Side Effects)If the first argument is a complex expression (e.g., Object.assign(getObj(), {y:2})), the fix incorrectly spreads getObj() instead of preserving the call and merging, changing evaluation and possibly
r2-p007fix okSpread Syntax Does Not Copy Property DescriptorsSpread syntax does not copy property descriptors (like getters/setters or non-enumerable flags), whereas Object.assign does, potentially changing object behavior.
r2-p008fix okNumeric/String Coercion with toString/valueOfnumeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt
r2-p009fix okName Collisions Across Scopes After a Movename collisions across scopes after a move (shadowing)
r2-p010fix okFunction Declarations Inside Blocks/Switch/Labeled Statementsfunction declarations inside blocks, switch cases, labeled statements
r2-p011fix okSparse Arrays, Holes, Array(n) vs [n]sparse arrays, holes, Array(n) vs [n], array-likes
r2-p012fix okComplex Expression as First Argument (Evaluation Order Side Effects)If the first argument is a complex expression (e.g., Object.assign(getObj(), {y:2})), the fix incorrectly spreads getObj() instead of preserving the call and merging, changing evaluation and possibly
r2-p013fix okPrecedence/Grouping in Larger ExpressionsWhen Object.assign is used as part of a larger expression (e.g., return Object.assign({}, x)), rewriting to ({ ...x }) may alter precedence or grouping if not parenthesized correctly.
r2-p014invalidClass Fields, Static Blocks, Private Names, Super Callsclass fields, static blocks, private names, super calls

Sources

r1-p002 · This Binding Hazard
1const obj = {
2 value: 42,
3 method() { return this.value; }
4};
5const fn = obj.method;
6console.log(Object.assign({}, { getValue: fn }).getValue());
--- orig.js	2026-10-22 14:29:06.539162401 -0400
+++ fixed.js	2026-10-22 14:29:08.676150847 -0400
@@ -3,4 +3,4 @@
   method() { return this.value; }
 };
 const fn = obj.method;
-console.log(Object.assign({}, { getValue: fn }).getValue());
+console.log(({ getValue: fn}).getValue());
undefined
r1-p003 · Getter/Setter Hazard
1let obj = {};
2Object.defineProperty(obj, 'prop', {
3 get() { return this._val; },
4 set(v) { this._val = v * 2; }
5});
6obj.prop = 5;
7const copied = Object.assign({}, obj);
8console.log(copied.prop, obj._val);
--- orig.js	2026-10-22 14:29:06.540162395 -0400
+++ fixed.js	2026-10-22 14:29:09.175148203 -0400
@@ -4,5 +4,5 @@
   set(v) { this._val = v * 2; }
 });
 obj.prop = 5;
-const copied = Object.assign({}, obj);
+const copied = { ...obj};
 console.log(copied.prop, obj._val);
undefined 10
r1-p004 · Temporal Dead Zone Hazard
1function test() {
2 try {
3 console.log(typeof x);
4 Object.assign({}, { x: 1 });
5 let x;
6 } catch (e) {
7 console.log(e.name);
8 }
9}
10test();
--- orig.js	2026-10-22 14:29:06.541162390 -0400
+++ fixed.js	2026-10-22 14:29:08.664150911 -0400
@@ -1,7 +1,7 @@
 function test() {
   try {
     console.log(typeof x);
-    Object.assign({}, { x: 1 });
+    ({ x: 1});
     let x;
   } catch (e) {
     console.log(e.name);
ReferenceError
r1-p005 · Sparse Array Hazard
1const arr = Array(3);
2arr[1] = 'a';
3const obj = Object.assign({}, arr);
4console.log(obj.length, obj[0], obj[1], obj[2]);
--- orig.js	2026-10-22 14:29:06.541162390 -0400
+++ fixed.js	2026-10-22 14:29:08.632151081 -0400
@@ -1,4 +1,4 @@
 const arr = Array(3);
 arr[1] = 'a';
-const obj = Object.assign({}, arr);
+const obj = { ...arr};
 console.log(obj.length, obj[0], obj[1], obj[2]);
undefined undefined a undefined
r1-p006 · Var Hoisting Hazard
1for (var i = 0; i < 2; i++) {
2 var obj = { x: i };
3 Object.assign({}, obj);
4}
5console.log(typeof obj, obj.x);
--- orig.js	2026-10-22 14:29:06.541162390 -0400
+++ fixed.js	2026-10-22 14:29:08.621151140 -0400
@@ -1,5 +1,5 @@
 for (var i = 0; i < 2; i++) {
   var obj = { x: i };
-  Object.assign({}, obj);
+  ({ ...obj});
 }
 console.log(typeof obj, obj.x);
object 1
r1-p007 · Null/Undefined Hazard
1let obj = null;
2try {
3 Object.assign({}, obj);
4} catch (e) {
5 console.log(e.name);
6}
7obj = undefined;
8try {
9 Object.assign({}, obj);
10} catch (e) {
11 console.log(e.name);
12}
--- orig.js	2026-10-22 14:29:06.542162384 -0400
+++ fixed.js	2026-10-22 14:29:08.692150762 -0400
@@ -1,12 +1,12 @@
 let obj = null;
 try {
-  Object.assign({}, obj);
+  ({ ...obj});
 } catch (e) {
   console.log(e.name);
 }
 obj = undefined;
 try {
-  Object.assign({}, obj);
+  ({ ...obj});
 } catch (e) {
   console.log(e.name);
 }
r1-p008 · Class Fields Hazard
1class Base {
2 static staticVal = 10;
3 instanceVal = 20;
4}
5class Derived extends Base {
6 static {
7 Derived.staticVal = Base.staticVal * 2;
8 }
9 method() {
10 return Object.assign({}, { staticVal: Derived.staticVal, instanceVal: this.instanceVal });
11 }
12}
13const d = new Derived();
14console.log(d.method().staticVal, d.method().instanceVal);
--- orig.js	2026-10-22 14:29:09.675145581 -0400
+++ fixed.js	2026-10-22 14:29:12.865129329 -0400
@@ -7,7 +7,7 @@
     Derived.staticVal = Base.staticVal * 2;
   }
   method() {
-    return Object.assign({}, { staticVal: Derived.staticVal, instanceVal: this.instanceVal });
+    return { staticVal: Derived.staticVal, instanceVal: this.instanceVal};
   }
 }
 const d = new Derived();
20 20
r2-p001 · Spread Syntax Does Not Copy Property Descriptors
1const obj = {};
2Object.defineProperty(obj, 'prop', {
3 get() { return this._value || 0; },
4 set(v) { this._value = v; },
5 enumerable: false
6});
7let result = Object.assign({}, obj);
8console.log(Object.getOwnPropertyDescriptor(result, 'prop')); // has getter/setter
9result = { ...obj };
10console.log(Object.getOwnPropertyDescriptor(result, 'prop')); // undefined
--- orig.js	2026-10-22 14:30:23.713981566 -0400
+++ fixed.js	2026-10-22 14:30:26.355983559 -0400
@@ -4,7 +4,7 @@
   set(v) { this._value = v; },
   enumerable: false
 });
-let result = Object.assign({}, obj);
+let result = { ...obj};
 console.log(Object.getOwnPropertyDescriptor(result, 'prop')); // has getter/setter
 result = { ...obj };
 console.log(Object.getOwnPropertyDescriptor(result, 'prop')); // undefined
undefined
undefined
r2-p002 · Numeric/String Coercion with toString/valueOf
1const obj = { valueOf() { return 42; }, toString() { return 'hello'; } };
2let sum = Object.assign({}, obj) + 8;
3console.log(sum); // 50 because valueOf called
4sum = ({ ...obj }) + 8;
5console.log(sum); // NaN because spread creates plain object, valueOf lost
--- orig.js	2026-10-22 14:30:23.714981567 -0400
+++ fixed.js	2026-10-22 14:30:28.172985224 -0400
@@ -1,5 +1,5 @@
 const obj = { valueOf() { return 42; }, toString() { return 'hello'; } };
-let sum = Object.assign({}, obj) + 8;
+let sum = ({ ...obj}) + 8;
 console.log(sum); // 50 because valueOf called
 sum = ({ ...obj }) + 8;
 console.log(sum); // NaN because spread creates plain object, valueOf lost
50
50
r2-p003 · Name Collisions Across Scopes After a Move
1function outer() {
2 const x = { a: 1 };
3 if (true) {
4 let x = { b: 2 }; // shadows outer x
5 console.log(Object.assign({}, x).a); // undefined, x is inner
6 console.log(({ ...x }).a); // undefined
7 }
8}
9outer();
--- orig.js	2026-10-22 14:30:23.715981567 -0400
+++ fixed.js	2026-10-22 14:30:28.103985154 -0400
@@ -2,7 +2,7 @@
   const x = { a: 1 };
   if (true) {
     let x = { b: 2 }; // shadows outer x
-    console.log(Object.assign({}, x).a); // undefined, x is inner
+    console.log(({ ...x}).a); // undefined, x is inner
     console.log(({ ...x }).a);           // undefined
   }
 }
undefined
undefined
r2-p004 · Function Declarations Inside Blocks/Switch/Labeled Statements
1switch (0) {
2 case 0:
3 function f() { return { y: 1 }; }
4 let r = Object.assign({}, f());
5 console.log(r.y); // 1
6 r = ({ ...f() });
7 console.log(r.y); // 1
8 break;
9}
--- orig.js	2026-10-22 14:30:23.716981568 -0400
+++ fixed.js	2026-10-22 14:30:28.157985209 -0400
@@ -1,7 +1,7 @@
 switch (0) {
   case 0:
     function f() { return { y: 1 }; }
-    let r = Object.assign({}, f());
+    let r = { ...f()};
     console.log(r.y); // 1
     r = ({ ...f() });
     console.log(r.y); // 1
1
1
r2-p005 · Sparse Arrays, Holes, Array(n) vs [n]
1const arr = Array(3);
2arr[1] = 'a';
3console.log(Object.assign({}, arr)); // { '1': 'a' }
4console.log({ ...arr }); // { '1': 'a' }
5// Both same for enum props, but test toString
6arr.toString = () => 'custom';
7console.log(String(Object.assign({}, arr))); // [object Object]
8console.log(String({ ...arr })); // [object Object]
--- orig.js	2026-10-22 14:30:23.716981568 -0400
+++ fixed.js	2026-10-22 14:30:26.945984068 -0400
@@ -1,8 +1,8 @@
 const arr = Array(3);
 arr[1] = 'a';
-console.log(Object.assign({}, arr)); // { '1': 'a' }
+console.log({ ...arr}); // { '1': 'a' }
 console.log({ ...arr });             // { '1': 'a' }
 // Both same for enum props, but test toString
 arr.toString = () => 'custom';
-console.log(String(Object.assign({}, arr))); // [object Object]
+console.log(String({ ...arr})); // [object Object]
 console.log(String({ ...arr }));            // [object Object]
{ '1': 'a' }
{ '1': 'a' }
custom
custom
r2-p006 · Complex Expression as First Argument (Evaluation Side Effects)
1let callCount = 0;
2function getObj() {
3 callCount++;
4 return { x: callCount };
5}
6let result = Object.assign(getObj(), { y: 2 });
7console.log(callCount, result.x, result.y); // 1, 1, 2
8result = ({ ...getObj(), y: 2 });
9console.log(callCount, result.x, result.y); // 2, 2, 2 (called twice)
1 1 2
2 2 2
r2-p007 · Spread Syntax Does Not Copy Property Descriptors
1const obj = {};
2Object.defineProperty(obj, 'prop', {
3 get() { return this._value || 0; },
4 set(v) { this._value = v; },
5 enumerable: false
6});
7let result = Object.assign({}, obj);
8console.log('desc:', Object.getOwnPropertyDescriptor(result, 'prop'));
9result = { ...obj };
10console.log('desc:', Object.getOwnPropertyDescriptor(result, 'prop'));
--- orig.js	2026-10-22 14:30:28.052985103 -0400
+++ fixed.js	2026-10-22 14:30:31.378988760 -0400
@@ -4,7 +4,7 @@
   set(v) { this._value = v; },
   enumerable: false
 });
-let result = Object.assign({}, obj);
+let result = { ...obj};
 console.log('desc:', Object.getOwnPropertyDescriptor(result, 'prop'));
 result = { ...obj };
 console.log('desc:', Object.getOwnPropertyDescriptor(result, 'prop'));
desc: undefined
desc: undefined
r2-p008 · Numeric/String Coercion with toString/valueOf
1const obj = { valueOf() { return 42; }, toString() { return 'hello'; } };
2let sum = Object.assign({}, obj) + 8;
3console.log('sum:', sum);
4sum = ({ ...obj }) + 8;
5console.log('sum:', sum);
--- orig.js	2026-10-22 14:30:28.144985196 -0400
+++ fixed.js	2026-10-22 14:30:31.378988760 -0400
@@ -1,5 +1,5 @@
 const obj = { valueOf() { return 42; }, toString() { return 'hello'; } };
-let sum = Object.assign({}, obj) + 8;
+let sum = ({ ...obj}) + 8;
 console.log('sum:', sum);
 sum = ({ ...obj }) + 8;
 console.log('sum:', sum);
sum: 50
sum: 50
r2-p009 · Name Collisions Across Scopes After a Move
1function outer() {
2 const x = { a: 1 };
3 if (true) {
4 let x = { b: 2 };
5 console.log('outer.x.a:', Object.assign({}, x).a);
6 console.log('outer.x.a:', ({ ...x }).a);
7 }
8}
9outer();
--- orig.js	2026-10-22 14:30:29.724986842 -0400
+++ fixed.js	2026-10-22 14:30:32.995990817 -0400
@@ -2,7 +2,7 @@
   const x = { a: 1 };
   if (true) {
     let x = { b: 2 };
-    console.log('outer.x.a:', Object.assign({}, x).a);
+    console.log('outer.x.a:', ({ ...x}).a);
     console.log('outer.x.a:', ({ ...x }).a);
   }
 }
outer.x.a: undefined
outer.x.a: undefined
r2-p010 · Function Declarations Inside Blocks/Switch/Labeled Statements
1switch (0) {
2 case 0:
3 function f() { return { y: 1 }; }
4 let r = Object.assign({}, f());
5 console.log('r.y:', r.y);
6 r = ({ ...f() });
7 console.log('r.y:', r.y);
8 break;
9}
--- orig.js	2026-10-22 14:30:29.796986920 -0400
+++ fixed.js	2026-10-22 14:30:32.417990065 -0400
@@ -1,7 +1,7 @@
 switch (0) {
   case 0:
     function f() { return { y: 1 }; }
-    let r = Object.assign({}, f());
+    let r = { ...f()};
     console.log('r.y:', r.y);
     r = ({ ...f() });
     console.log('r.y:', r.y);
r.y: 1
r.y: 1
r2-p011 · Sparse Arrays, Holes, Array(n) vs [n]
1const arr = Array(3);
2arr[1] = 'a';
3console.log('assigned:', Object.assign({}, arr));
4console.log('spread:', { ...arr });
5arr.toString = () => 'custom';
6console.log('String assigned:', String(Object.assign({}, arr)));
7console.log('String spread:', String({ ...arr }));
--- orig.js	2026-10-22 14:30:29.796986920 -0400
+++ fixed.js	2026-10-22 14:30:32.414990062 -0400
@@ -1,7 +1,7 @@
 const arr = Array(3);
 arr[1] = 'a';
-console.log('assigned:', Object.assign({}, arr));
+console.log('assigned:', { ...arr});
 console.log('spread:', { ...arr });
 arr.toString = () => 'custom';
-console.log('String assigned:', String(Object.assign({}, arr)));
+console.log('String assigned:', String({ ...arr}));
 console.log('String spread:', String({ ...arr }));
assigned: { '1': 'a' }
spread: { '1': 'a' }
String assigned: custom
String spread: custom
r2-p012 · Complex Expression as First Argument (Evaluation Order Side Effects)
1let state = 0;
2function getObj() {
3 state++;
4 return { x: state };
5}
6let result = Object.assign({x: state}, {y: state}, getObj());
7console.log('state:', state, 'result:', result);
8result = ({x: state, y: state, ...getObj()});
9console.log('state:', state, 'result:', result);
--- orig.js	2026-10-22 14:30:30.199987371 -0400
+++ fixed.js	2026-10-22 14:30:34.486992899 -0400
@@ -3,7 +3,7 @@
   state++;
   return { x: state };
 }
-let result = Object.assign({x: state}, {y: state}, getObj());
+let result = {x: state, y: state, ...getObj()};
 console.log('state:', state, 'result:', result);
 result = ({x: state, y: state, ...getObj()});
 console.log('state:', state, 'result:', result);
state: 1 result: { x: 1, y: 0 }
state: 2 result: { x: 2, y: 1 }
r2-p013 · Precedence/Grouping in Larger Expressions
1let x = {a: 1};
2let y = Object.assign({}, x) + 2;
3console.log('y:', y);
4y = ({ ...x }) + 2;
5console.log('y:', y);
--- orig.js	2026-10-22 14:30:33.015990844 -0400
+++ fixed.js	2026-10-22 14:30:36.226995518 -0400
@@ -1,5 +1,5 @@
 let x = {a: 1};
-let y = Object.assign({}, x) + 2;
+let y = ({ ...x}) + 2;
 console.log('y:', y);
 y = ({ ...x }) + 2;
 console.log('y:', y);
y: [object Object]2
y: [object Object]2
r2-p014 · Class Fields, Static Blocks, Private Names, Super Calls (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-28-5)
1class Base {
2 constructor() { this.public = 1; }
3}
4class Derived extends Base {
5 #private = 2;
6 static staticField = 3;
7 constructor() {
8 super();
9 console.log('super.public:', Object.assign({}, super).public);
10 console.log('super.public:', ({ ...super }).public);
11 }
12}
13new Derived();