biome style/useConsistentObjectDefinitions
labelled safe
Analysis Nemotron 3 Super
The rule flags inconsistent usage of object literal property syntax by requiring either all properties to be written explicitly (`key: value`) or using shorthand notation (`key`) when the variable name matches the property key.
Fix: When the option 'syntax' is 'explicit', the autofix replaces shorthand property notation (e.g., `foo`) with its explicit equivalent (`foo: foo`) by duplicating the identifier as both the property key and value. When the option is 'shorthand', it replaces explicit notation where the key and value are identical identifiers (e.g., `foo: foo`) with the shorthand form (`foo`).
10 hazards
- Rewriting `foo: foo` to `foo` in a `with` statement changes the object used for property lookup, potentially altering which properties are resolved due to differing object identity.
- In `try/catch`, converting `{e: e}` to `{e}` may cause the catch clause's parameter `e` to shadow the property value incorrectly if the rewrite occurs inside the catch block, changing which variable is referenced.
- Within a `for` loop's init or update clause, replacing `{i: i}` with `{i}` could affect timing if `i` has side effects (though unlikely for identifiers), but more critically, if `i` is declared with `var` in an outer scope, the shorthand may capture a different binding due to hoisting differences in evaluation context.
- If the property value is a getter or setter (e.g., `get foo() {}`), rewriting to shorthand `foo` is invalid syntax and will cause a parse error, as shorthand does not support accessor properties.
- When the object literal is used as the right-hand side of a comma operator (e.g., `let x = (y, {z: z});`), rewriting `{z: z}` to `{z}` does not change the value but may interfere with formatting tools that rely on explicit syntax for consistency, though semantically it remains equivalent.
- In class fields, transforming `{method: method}` to `{method}` may break if `method` is not defined in the enclosing scope due to lexical scoping differences, though this is rare as class fields evaluate in the class scope.
- If the identifier used in shorthand is later reassigned (e.g., `let f = 1; const o = {f}; f = 2;`), the object property retains the original value at the time of creation, which is preserved in the rewrite, so no hazard here—this is included to show that reassignment after object creation does not affect the equivalence.
- Comments attached to the colon in explicit syntax (e.g., `foo: /* comment */ bar`) will be lost when rewriting to shorthand `foo`, as the autofix removes the colon and any surrounding whitespace/comments.
- When the property key is an identifier but the value is a complex expression (e.g., `foo: bar + baz`), the rule does not fire, so no rewrite occurs—but if misapplied, rewriting such a case to shorthand would be a syntax error, highlighting that the fix only applies when key and value are the same identifier.
- In non-strict mode, using `with` or `eval`, the rewritten shorthand may interact differently with scope chains if the object is used in a context where property resolution depends on the object's internal structure, though this is highly esoteric and unlikely.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | fix ok | Catch clause parameter shadowing | In try/catch, converting {e: e} to {e} may cause the catch clause's parameter e to shadow the property value incorrectly if the rewrite occurs inside the catch block, changing which variable is refere |
| r1-p002 | invalid | With statement object identity | Rewriting foo: foo to foo in a with statement changes the object used for property lookup, potentially altering which properties are resolved due to differing object identity. |
| r1-p003 | fix ok | Null/undefined distinction with optional chaining | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
| r1-p004 | silent prediction off | Order of evaluation with side effects | order of evaluation when expressions are merged, duplicated or deleted (side effects run twice or never) |
| r1-p005 | silent | Class field lexical scoping | In class fields, transforming {method: method} to {method} may break if method is not defined in the enclosing scope due to lexical scoping differences, though this is rare as class fields evaluate in |
| r1-p006 | fix ok | Comments on colon lost in rewrite | Comments attached to the colon in explicit syntax (e.g., foo: /* comment */ bar) will be lost when rewriting to shorthand foo, as the autofix removes the colon and any surrounding whitespace/comments. |
| r1-p007 | fix ok | Comments attached to removed/moved code | comments attached to the removed or moved code |
| r1-p008 | fix ok | Arguments object and function length | arguments object, function.length, default parameters, rest parameters |
| r2-p001 | fix ok | var hoisting and re-declaration | var hoisting and re-declaration inside loops or after use |
| r2-p002 | fix ok | comma operator RHS | When the object literal is used as the right-hand side of a comma operator (e.g., `let x = (y, {z: z});`), rewriting `{z: z}` to `{z}` does not change the value but may interfere with formatting tools |
| r2-p003 | silent | complex value expression | When the property key is an identifier but the value is a complex expression (e.g., `foo: bar + baz`), the rule does not fire, so no rewrite occurs—but if misapplied, rewriting such a case to shorthan |
| r2-p004 | invalid | with statement interaction | In non-strict mode, using `with` or `eval`, the rewritten shorthand may interact differently with scope chains if the object is used in a context where property resolution depends on the object's inte |
| r2-p005 | fix ok | strict vs sloppy mode semantics | strict vs sloppy mode semantics |
| r2-p006 | fix ok | reassignment after creation | If the identifier used in shorthand is later reassigned (e.g., `let f = 1; const o = {f}; f = 2;`), the object property retains the original value at the time of creation, which is preserved in the re |
| r2-p007 | fix ok | null/undefined distinction with optional chaining | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
| r2-p008 | fix ok | sparse arrays and array-likes | sparse arrays, holes, Array(n) vs [n], array-likes |
Sources
r1-p001 · Catch clause parameter shadowing
1function outer() {
2 var e = 'outer';
3 try {
4 throw new Error('test');
5 } catch (e) {
6 // Biome would rewrite {e: e} -> {e}
7 const obj = {e: e};
8 console.log(obj.e);
9 }
10 return e;
11}
12console.log(outer());
--- orig.js 2026-10-17 17:00:54.196133769 -0400 +++ fixed.js 2026-10-17 17:00:54.465137084 -0400 @@ -4,7 +4,7 @@ throw new Error('test'); } catch (e) { // Biome would rewrite {e: e} -> {e} - const obj = {e: e}; + const obj = {e}; console.log(obj.e); } return e;
Error: test
at outer (<wd>/prog)
at <wd>/prog
at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
at async node:internal/modules/esm/loader:650:26
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)
outerr1-p002 · With statement object identity (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-00-3)
1var x = 1;
2var objA = {x: x};
3var objB = {x};
4with (objA) {
5 console.log(x); // logs 1 from objA.x
6}
7with (objB) {
8 console.log(x); // logs undefined because x not found (no property named 'x')
9}
r1-p003 · Null/undefined distinction with optional chaining
1let value = null;
2const obj = {value: value};
3// After fix: {value}
4console.log(obj.value ?? 'nullish');
5console.log((obj.value ?? 'nullish') === 'nullish');
6value = undefined;
7console.log(obj.value ?? 'nullish');
8console.log((obj.value ?? 'nullish') === 'nullish');
--- orig.js 2026-10-17 17:00:54.675139671 -0400 +++ fixed.js 2026-10-17 17:00:54.937142900 -0400 @@ -1,5 +1,5 @@ let value = null; -const obj = {value: value}; +const obj = {value}; // After fix: {value} console.log(obj.value ?? 'nullish'); console.log((obj.value ?? 'nullish') === 'nullish');
nullish true nullish true
r1-p004 · Order of evaluation with side effects
1let count = 0;
2function side() {
3 return ++count;
4}
5const obj = {x: side(), y: side()};
6// If rewritten to {x, y} but only once evaluated? Not applicable.
7// Instead: duplicate eval in explicit form
8const obj2 = {a: side(), a: side()};
9console.log(count); // Should be 2 if both sides evaluated
10console.log(obj2.a); // Last wins: 2
4 4
r1-p005 · Class field lexical scoping
1class C {
2 method = () => 'hi';
3 static field = {
4 // Biome does not trigger here: method not same name as key
5 other: this.method
6 };
7}
8console.log(C.field.other());
r1-p006 · Comments on colon lost in rewrite
1let foo = 42;
2// Comment before
3const obj = {foo: /* important */ foo};
4// After fix: {foo} — comment lost
5console.log(obj.foo);
--- orig.js 2026-10-17 17:00:55.545150389 -0400 +++ fixed.js 2026-10-17 17:00:55.817153739 -0400 @@ -1,5 +1,5 @@ let foo = 42; // Comment before -const obj = {foo: /* important */ foo}; +const obj = {foo}; // After fix: {foo} — comment lost console.log(obj.foo);
42
r1-p007 · Comments attached to removed/moved code
1let a = 1;
2// This comment is on the line with the shorthand candidate
3const obj = {/* keep me */ a: a, b: 2};
4// After fix: {/* keep me */ a, b: 2} — comment preserved? Autofix may keep it.
5// But if it deletes the whole prop, comment goes.
6// We test by having comment on the prop being removed.
7console.log(obj.a);
--- orig.js 2026-10-17 17:00:55.985155809 -0400 +++ fixed.js 2026-10-17 17:00:56.252159096 -0400 @@ -1,6 +1,6 @@ let a = 1; // This comment is on the line with the shorthand candidate -const obj = {/* keep me */ a: a, b: 2}; +const obj = {/* keep me */ a, b: 2}; // After fix: {/* keep me */ a, b: 2} — comment preserved? Autofix may keep it. // But if it deletes the whole prop, comment goes. // We test by having comment on the prop being removed.
1
r1-p008 · Arguments object and function length
1function f(a, b) {
2 // Biome would rewrite {a: a, b: b} -> {a, b}
3 const obj = {a: a, b: b};
4 console.log(obj.a + ', ' + obj.b);
5 console.log(arguments.length);
6}
7f(10, 20);
--- orig.js 2026-10-17 17:00:56.419161152 -0400 +++ fixed.js 2026-10-17 17:00:56.691164501 -0400 @@ -1,6 +1,6 @@ function f(a, b) { // Biome would rewrite {a: a, b: b} -> {a, b} - const obj = {a: a, b: b}; + const obj = {a, b}; console.log(obj.a + ', ' + obj.b); console.log(arguments.length); }
10, 20 2
r2-p001 · var hoisting and re-declaration
1for (var i = 0; i < 2; i++) {
2 var j = i;
3 var k = {j: j};
4}
5console.log(typeof k === 'object' && k !== null ? k.j : 'null');
--- orig.js 2026-10-17 17:01:03.008242154 -0400 +++ fixed.js 2026-10-17 17:01:03.285245553 -0400 @@ -1,5 +1,5 @@ for (var i = 0; i < 2; i++) { var j = i; - var k = {j: j}; + var k = {j}; } console.log(typeof k === 'object' && k !== null ? k.j : 'null');
1
r2-p002 · comma operator RHS
1let y = 0;
2let x = (y++, {z: z});
3function z() { return 42; }
4console.log(typeof x === 'object' && x !== null && typeof x.z === 'function' ? x.z() : -1);
--- orig.js 2026-10-17 17:01:03.456247652 -0400 +++ fixed.js 2026-10-17 17:01:03.734251064 -0400 @@ -1,4 +1,4 @@ let y = 0; -let x = (y++, {z: z}); +let x = (y++, {z}); function z() { return 42; } console.log(typeof x === 'object' && x !== null && typeof x.z === 'function' ? x.z() : -1);
42
r2-p003 · complex value expression
1let foo = 1;
2let bar = 2;
3let baz = 3;
4let obj = {foo: bar + baz};
5console.log(obj.foo);
5
r2-p004 · with statement interaction (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T21-00-3)
1(function() {
2 'use sloppy';
3 let obj = {prop: prop};
4 let prop = 99;
5 with (obj) {
6 console.log(prop);
7 }
8})();
r2-p005 · strict vs sloppy mode semantics
1'use strict';
2let a = 1;
3let obj = {a: a};
4console.log(obj.a);
5'use sloppy';
6let b = 2;
7let obj2 = {b: b};
8console.log(obj2.b);
--- orig.js 2026-10-17 17:01:04.181256548 -0400 +++ fixed.js 2026-10-17 17:01:04.461259983 -0400 @@ -1,8 +1,8 @@ 'use strict'; let a = 1; -let obj = {a: a}; +let obj = {a}; console.log(obj.a); 'use sloppy'; let b = 2; -let obj2 = {b: b}; +let obj2 = {b}; console.log(obj2.b);
1 2
r2-p006 · reassignment after creation
1let f = 1;
2const o = {f: f};
3f = 2;
4console.log(o.f);
--- orig.js 2026-10-17 17:01:04.629262044 -0400 +++ fixed.js 2026-10-17 17:01:04.903265405 -0400 @@ -1,4 +1,4 @@ let f = 1; -const o = {f: f}; +const o = {f}; f = 2; console.log(o.f);
1
r2-p007 · null/undefined distinction with optional chaining
1let maybe = undefined;
2let obj = {maybe: maybe};
3console.log(obj.maybe ?? 'default');
4try {
5 console.log(obj.nonexistent?.prop);
6} catch (e) {
7 console.log('caught:', e.name);
8}
--- orig.js 2026-10-17 17:01:05.086267649 -0400 +++ fixed.js 2026-10-17 17:01:05.353270923 -0400 @@ -1,5 +1,5 @@ let maybe = undefined; -let obj = {maybe: maybe}; +let obj = {maybe}; console.log(obj.maybe ?? 'default'); try { console.log(obj.nonexistent?.prop);
default undefined
r2-p008 · sparse arrays and array-likes
1let arr = new Array(3);
2arr[1] = 'present';
3let obj = {arr: arr};
4console.log('length:', obj.arr.length);
5console.log('0 in obj.arr:', 0 in obj.arr);
6console.log('1 in obj.arr:', 1 in obj.arr);
7console.log('2 in obj.arr:', 2 in obj.arr);
--- orig.js 2026-10-17 17:01:05.517272933 -0400 +++ fixed.js 2026-10-17 17:01:05.782276183 -0400 @@ -1,6 +1,6 @@ let arr = new Array(3); arr[1] = 'present'; -let obj = {arr: arr}; +let obj = {arr}; console.log('length:', obj.arr.length); console.log('0 in obj.arr:', 0 in obj.arr); console.log('1 in obj.arr:', 1 in obj.arr);
length: 3 0 in obj.arr: false 1 in obj.arr: true 2 in obj.arr: false