fixhunt

biome style/useObjectSpread

labelled safe
Runs / 2026-10-17T21-06-40-985Z-tm0a-biome-style_useObjectSpreadbiome 2.5.152026-10-17 21:06 UTC8 programs × 2 round(s)16 programs, 13 fired, 13 fixed, 0 findings, 1 rejectedmodel cost $0.0032

Analysis Nemotron 3 Super

The rule flags uses of Object.assign() where the first argument is an empty object literal {}, suggesting replacement with object spread syntax.

Fix: Replace Object.assign({}, ...) with an object literal using spread properties: {...args} where args are all arguments after the first empty object.

10 hazards
  • If the first argument is not a literal {} but a variable or expression that evaluates to an empty object, the fix incorrectly assumes it is a literal and removes it, altering behavior.
  • If Object.assign is called via a different reference (e.g., const assign = Object.assign; assign({}, foo)), the fix does not apply and may leave unsafe code unchanged, but if misapplied, it would break due to missing context.
  • If the first argument is {} but followed by non-object primitives (e.g., Object.assign({}, null, foo)), the spread fix may throw at runtime because spreading null/undefined is invalid, whereas Object.assign coerces them.
  • If the Object.assign call is inside a larger expression and the fix removes parentheses needed for grouping (e.g., return Object.assign({}, foo); → return {...foo}; is safe, but (Object.assign({}, foo)) → ({...foo}) may be needed in some contexts like JSX props), the fix might omit necessary parentheses.
  • If the code relies on the side effect of Object.assign defining properties on the first argument (even if it's {}), and that object is later used elsewhere, the fix changes when the object is created (in-place vs new), potentially causing aliasing issues.
  • If the first argument {} is actually a destructured or computed property in an object literal (e.g., {[k]: Object.assign({}, foo)}), the fix could produce invalid syntax or change evaluation order.
  • If the Object.assign call is in a loop or frequently executed path and the environment lacks spread support (e.g., IE without transpilation), the fix may introduce syntax errors in older environments.
  • If the arguments after the first include getters or have side effects, the evaluation order of spreading vs Object.assign is the same (left-to-right), so no issue, but if the fix were to reorder args (it doesn't), it would matter—however, Biome's fix preserves order.
  • If the code contains trailing comments on the Object.assign line, the fix might discard them if not properly preserved during replacement.
  • If the first argument {} is actually part of a larger object literal spread (e.g., {...other, ...Object.assign({}, foo)}), replacing the inner call could break nesting or change the resulting object structure due to duplicate key handling.

Programs

idresulttitlehazard
r1-p001fix okASI and semicolon-less codeASI and semicolon-less code, leading ( or [ on the next line
r1-p002fix okcomments attached to removed/moved codecomments attached to the removed or moved code
r1-p003invalidexceptions inside rewritten codeexceptions thrown inside the code the fix removes or rewrites
r1-p004fix okvar hoisting and re-declarationvar hoisting and re-declaration inside loops or after use
r1-p005fix okname collisions after move (shadowing)name collisions across scopes after a move (shadowing)
r1-p006fix okpreserved arg order with side effectsIf the arguments after the first include getters or have side effects, the evaluation order of spreading vs Object.assign is the same (left-to-right), so no issue, but if the fix were to reorder args
r1-p007fix okclass fields, static blocks, private names, super callsclass fields, static blocks, private names, super calls
r1-p008fix okfunction declarations inside blocks, switch cases, labeled statementsfunction declarations inside blocks, switch cases, labeled statements
r2-p001fix okGetter setter defineProperty side effectsgetters, setters and Object.defineProperty side effects
r2-p002fix okSparse array array-like side effectssparse arrays, holes, Array(n) vs [n], array-likes
r2-p003silentMisapplied fix via alias referenceIf Object.assign is called via a different reference (e.g., const assign = Object.assign; assign({}, foo)), the fix does not apply and may leave unsafe code unchanged, but if misapplied, it would brea
r2-p004fix okComputed property key in object literalIf the first argument {} is actually a destructured or computed property in an object literal (e.g., {[k]: Object.assign({}, foo)}), the fix could produce invalid syntax or change evaluation order.
r2-p005silent prediction offAliasing issue due to changed object creation timingIf the code relies on the side effect of Object.assign defining properties on the first argument (even if it's {}), and that object is later used elsewhere, the fix changes when the object is created
r2-p006fix okArguments object and rest parameters interactionarguments object, function.length, default parameters, rest parameters
r2-p007fix okName collision after move shadowingname collisions across scopes after a move (shadowing)
r2-p008fix okException inside rewritten codeexceptions thrown inside the code the fix removes or rewrites

Sources

r1-p001 · ASI and semicolon-less code
1let result
2(() => {
3 const r = Object.assign({}, {x: 1})
4 return r.x
5})().then(v => {
6 console.log('result:', v)
7}).catch(e => {
8 console.error('error:', e.message)
9});
10[].forEach.call([], () => {})
--- orig.js	2026-10-17 17:06:51.040241668 -0400
+++ fixed.js	2026-10-17 17:06:51.319244699 -0400
@@ -1,6 +1,6 @@
 let result
 (() => {
-  const r = Object.assign({}, {x: 1})
+  const r = ({x: 1,})
   return r.x
 })().then(v => {
   console.log('result:', v)
r1-p002 · comments attached to removed/moved code
1// Setup
2const input = {a: 1}
3// Transform
4const out = /* comment */Object.assign(/* another */{}, input)// end
5console.log(out.a)
--- orig.js	2026-10-17 17:06:51.488246536 -0400
+++ fixed.js	2026-10-17 17:06:51.772249622 -0400
@@ -1,5 +1,5 @@
 // Setup
 const input = {a: 1}
 // Transform
-const out = /* comment */Object.assign(/* another */{}, input)// end
+const out = /* comment */({...input,})// end
 console.log(out.a)
1
r1-p003 · exceptions inside rewritten code (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-06-4)
1try {
2 const o = Object.assign({}, {get x(){throw new Error('boom')}})
3} catch (e) {
4 console.log(e.name + ': ' + e.message)
5}
6try {
7 const p = {/*spread*/}{get x(){throw new Error('boom')}}
8} catch (e) {
9 console.log(e.name + ': ' + e.message)
10}
r1-p004 · var hoisting and re-declaration
1function f() {
2 for (var i = 0; i < 2; i++) {
3 var tmp = Object.assign({}, {val: i})
4 console.log(tmp.val)
5 }
6 console.log(typeof tmp)
7}
8f()
--- orig.js	2026-10-17 17:06:51.978251860 -0400
+++ fixed.js	2026-10-17 17:06:52.252254837 -0400
@@ -1,6 +1,6 @@
 function f() {
   for (var i = 0; i < 2; i++) {
-    var tmp = Object.assign({}, {val: i})
+    var tmp = ({val: i,})
     console.log(tmp.val)
   }
   console.log(typeof tmp)
0
1
object
r1-p005 · name collisions after move (shadowing)
1let data = {prop: 'outer'}
2{
3 let data = Object.assign({}, {inner: 'value'})
4 console.log(data.prop, data.inner)
5}
6console.log(data.prop)
--- orig.js	2026-10-17 17:06:52.418256640 -0400
+++ fixed.js	2026-10-17 17:06:52.675259432 -0400
@@ -1,6 +1,6 @@
 let data = {prop: 'outer'}
 {
-  let data = Object.assign({}, {inner: 'value'})
+  let data = ({inner: 'value',})
   console.log(data.prop, data.inner)
 }
 console.log(data.prop)
undefined value
outer
r1-p006 · preserved arg order with side effects
1let seq = []
2const obj1 = {get a(){seq.push(1); return 1}}
3const obj2 = {get b(){seq.push(2); return 2}}
4const res = Object.assign({}, obj1, obj2)
5console.log('seq:', seq.join(', '), 'res:', JSON.stringify(res))
--- orig.js	2026-10-17 17:06:52.844261268 -0400
+++ fixed.js	2026-10-17 17:06:53.121264276 -0400
@@ -1,5 +1,5 @@
 let seq = []
 const obj1 = {get a(){seq.push(1); return 1}}
 const obj2 = {get b(){seq.push(2); return 2}}
-const res = Object.assign({}, obj1, obj2)
+const res = ({...obj1,...obj2,})
 console.log('seq:', seq.join(', '), 'res:', JSON.stringify(res))
seq: 1, 2 res: {"a":1,"b":2}
r1-p007 · class fields, static blocks, private names, super calls
1class Base {
2 static baseProp = 'base'
3}
4class Derived extends Base {
5 static staticProp = Object.assign({}, {derived: true})
6 static {
7 console.log(Base.baseProp, Derived.staticProp.derived)
8 }
9 #private = Object.assign({}, {secret: 42})
10 constructor() {
11 super()
12 console.log(this.#private.secret)
13 }
14}
15new Derived()
--- orig.js	2026-10-17 17:06:53.297266188 -0400
+++ fixed.js	2026-10-17 17:06:53.576269218 -0400
@@ -2,11 +2,11 @@
   static baseProp = 'base'
 }
 class Derived extends Base {
-  static staticProp = Object.assign({}, {derived: true})
+  static staticProp = ({derived: true,})
   static {
     console.log(Base.baseProp, Derived.staticProp.derived)
   }
-  #private = Object.assign({}, {secret: 42})
+  #private = ({secret: 42,})
   constructor() {
     super()
     console.log(this.#private.secret)
base true
42
r1-p008 · function declarations inside blocks, switch cases, labeled statements
1start: {
2 if (true) {
3 function helper() {
4 return Object.assign({}, {flag: true})
5 }
6 const h = helper()
7 console.log(h.flag)
8 break start
9 }
10 switch (0) {
11 case 0:
12 function caseFn() {
13 return Object.assign({}, {case: 0})
14 }
15 console.log(caseFn().case)
16 break
17 }
18}
--- orig.js	2026-10-17 17:06:53.749271097 -0400
+++ fixed.js	2026-10-17 17:06:54.033274181 -0400
@@ -1,7 +1,7 @@
 start: {
   if (true) {
     function helper() {
-      return Object.assign({}, {flag: true})
+      return ({flag: true,})
     }
     const h = helper()
     console.log(h.flag)
@@ -10,7 +10,7 @@
   switch (0) {
     case 0:
       function caseFn() {
-        return Object.assign({}, {case: 0})
+        return ({case: 0,})
       }
       console.log(caseFn().case)
       break
true
r2-p001 · Getter setter defineProperty side effects
1const target = {};
2Object.defineProperty(target, 'prop', {
3 get() { console.log('GET'); return 42; },
4 configurable: true
5});
6console.log('before:', typeof target.prop);
7Object.assign({}, target); // triggers rule
8console.log('after:', typeof target.prop);
--- orig.js	2026-10-17 17:06:59.358331965 -0400
+++ fixed.js	2026-10-17 17:06:59.630334914 -0400
@@ -4,5 +4,5 @@
   configurable: true
 });
 console.log('before:', typeof target.prop);
-Object.assign({}, target); // triggers rule
+({...target,}); // triggers rule
 console.log('after:', typeof target.prop);
GET
before: number
GET
after: number
r2-p002 · Sparse array array-like side effects
1const arr = Array(3);
2arr[1] = 'present';
3console.log('sparse length:', arr.length);
4console.log('sparse [1]:', arr[1]);
5const copy = Object.assign({}, arr); // triggers rule
6console.log('copy length:', copy.length);
7console.log('copy [1]:', copy[1]);
--- orig.js	2026-10-17 17:06:59.800336757 -0400
+++ fixed.js	2026-10-17 17:07:00.080339793 -0400
@@ -2,6 +2,6 @@
 arr[1] = 'present';
 console.log('sparse length:', arr.length);
 console.log('sparse [1]:', arr[1]);
-const copy = Object.assign({}, arr); // triggers rule
+const copy = ({...arr,}); // triggers rule
 console.log('copy length:', copy.length);
 console.log('copy [1]:', copy[1]);
sparse length: 3
sparse [1]: present
copy length: undefined
copy [1]: present
r2-p003 · Misapplied fix via alias reference
1const assign = Object.assign;
2const result = assign({}, {x: 1}); // not triggered by rule
3console.log('result.x:', result.x);
4// Biome will NOT suggest fix here because it's not direct Object.assign call
result.x: 1
r2-p004 · Computed property key in object literal
1let key = 'dynamic';
2const obj = {
3 [key]: Object.assign({}, {a: 1}) // triggers rule
4};
5console.log(obj.dynamic.a);
6key = 'changed';
7console.log(obj.changed === undefined ? 'not present' : 'present');
--- orig.js	2026-10-17 17:07:00.481344141 -0400
+++ fixed.js	2026-10-17 17:07:00.740346948 -0400
@@ -1,6 +1,6 @@
 let key = 'dynamic';
 const obj = {
-  [key]: Object.assign({}, {a: 1}) // triggers rule
+  [key]: ({a: 1,}) // triggers rule
 };
 console.log(obj.dynamic.a);
 key = 'changed';
1
not present
r2-p005 · Aliasing issue due to changed object creation timing
1let shared = {};
2function getTarget() { return shared; }
3const obj1 = Object.assign(getTarget(), {x: 1}); // triggers rule
4console.log('shared.x after assign:', shared.x);
5shared = {};
6const obj2 = Object.assign(getTarget(), {y: 2}); // triggers rule
7console.log('shared.y after second assign:', shared.y);
shared.x after assign: 1
shared.y after second assign: 2
r2-p006 · Arguments object and rest parameters interaction
1function test(a, b, c) {
2 console.log('args length:', arguments.length);
3 const copied = Object.assign({}, arguments); // triggers rule
4 console.log('copied 0:', copied[0]);
5 return copied;
6}
7const result = test(10, 20, 30);
8console.log('result 2:', result[2]);
--- orig.js	2026-10-17 17:07:01.190351826 -0400
+++ fixed.js	2026-10-17 17:07:01.460354753 -0400
@@ -1,6 +1,6 @@
 function test(a, b, c) {
   console.log('args length:', arguments.length);
-  const copied = Object.assign({}, arguments); // triggers rule
+  const copied = ({...arguments,}); // triggers rule
   console.log('copied 0:', copied[0]);
   return copied;
 }
args length: 3
copied 0: 10
result 2: 30
r2-p007 · Name collision after move shadowing
1let obj = {x: 1};
2if (true) {
3 let obj = {y: 2}; // inner obj
4 Object.assign({}, obj); // triggers rule, copies inner {y:2}
5 console.log('inner:', obj.y);
6}
7console.log('outer:', obj.x); // outer obj unchanged
--- orig.js	2026-10-17 17:07:01.626356552 -0400
+++ fixed.js	2026-10-17 17:07:01.907359597 -0400
@@ -1,7 +1,7 @@
 let obj = {x: 1};
 if (true) {
   let obj = {y: 2}; // inner obj
-  Object.assign({}, obj); // triggers rule, copies inner {y:2}
+  ({...obj,}); // triggers rule, copies inner {y:2}
   console.log('inner:', obj.y);
 }
 console.log('outer:', obj.x); // outer obj unchanged
inner: 2
outer: 1
r2-p008 · Exception inside rewritten code
1function risky() {
2 throw new TypeError('intentional');
3}
4try {
5 Object.assign({}, {a: risky()}); // triggers rule, risky() throws
6} catch (e) {
7 console.log('caught:', e.name, e.message);
8}
9console.log('continued');
--- orig.js	2026-10-17 17:07:02.074361407 -0400
+++ fixed.js	2026-10-17 17:07:02.348364376 -0400
@@ -2,7 +2,7 @@
   throw new TypeError('intentional');
 }
 try {
-  Object.assign({}, {a: risky()}); // triggers rule, risky() throws
+  ({a: risky(),}); // triggers rule, risky() throws
 } catch (e) {
   console.log('caught:', e.name, e.message);
 }
caught: TypeError intentional
continued