eslint no-useless-computed-key
labelled safe
Analysis Nemotron 3 Super
Flags unnecessary computed property keys using string/numeric literals that can be replaced with literal property keys.
Fix: Replace computed property key ["literal"] with "literal" (quoted string) or [number] with number (unquoted numeric literal), preserving spacing and syntax structure.
8 hazards
- Rewriting ["__proto__"] to "__proto__" changes behavior from defining a property named "__proto__" to setting the object's prototype, altering inheritance.
- If the computed key contains an escape sequence like ["a\nb"], rewriting to "a\nb" may alter the key if the escape was not normalized in the source.
- In class fields, rewriting ["constructor"] to "constructor" may conflict with ES6 class constructor semantics if the field is intended as a regular property.
- Rewriting a computed key in a destructuring pattern like {[0]: foo} to {0: foo} may fail if the key is not a valid identifier and the target environment lacks numeric property support (though rare).
- If the computed key is adjacent to a comment inside the brackets, removing the brackets may detach or lose the comment during AST rewrite.
- In class static fields, rewriting ["prototype"] to "prototype" may cause a runtime error because static prototype is forbidden, whereas the computed form might have been intended to avoid early error (though unlikely).
- If the object literal is used in a context where property order matters for iteration (e.g., legacy engines), changing from computed to literal key could alter enumeration order if the literal key sorts differently.
- Rewriting ["0"] to 0 in an object key may cause issues if the code relies on the key being a string for typeof key === 'string' checks, though the property access behavior remains the same.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | invalid | Escape sequences in computed keys | If the computed key contains an escape sequence like ["a\nb"], rewriting to "a\nb" may alter the key if the escape was not normalized in the source. |
| r1-p002 | silent prediction off | Numeric keys in destructuring patterns without numeric property support | Rewriting a computed key in a destructuring pattern like {[0]: foo} to {0: foo} may fail if the key is not a valid identifier and the target environment lacks numeric property support. |
| r1-p003 | silent prediction off | Function declarations inside blocks, switch cases, labeled statements | function declarations inside blocks, switch cases, labeled statements |
| r1-p004 | silent prediction off | String vs number key distinction in typeof checks | Rewriting ["0"] to 0 in an object key may cause issues if the code relies on the key being a string for typeof key === 'string' checks, though the property access behavior remains the same. |
| r1-p005 | silent prediction off | Computed __proto__ key altering prototype | Rewriting ["__proto__"] to "__proto__" changes behavior from defining a property named "__proto__" to setting the object's prototype, altering inheritance. |
| r1-p006 | fix ok | Label/break/continue control flow with computed keys | label/break/continue control flow, finally blocks, early returns in nested blocks |
| r1-p007 | fix ok | Temporal dead zone after hoisting/reordering | temporal dead zone after code is hoisted or reordered |
| r1-p008 | fix ok | Comments attached to removed/moved code | comments attached to the removed or moved code |
| r2-p001 | silent prediction off | Class field named 'constructor' conflicts with actual constructor | In class fields, rewriting ["constructor"] to "constructor" may conflict with ES6 class constructor semantics if the field is intended as a regular property. |
| r2-p002 | fix ok | Temporal dead zone after hoisting/reordering in destructuring | temporal dead zone after code is hoisted or reordered |
| r2-p003 | silent prediction off | Var hoisting and redeclaration inside loop after use | var hoisting and re-declaration inside loops or after use |
| r2-p004 | fired, no fix | Comment inside brackets detached during bracket removal | If the computed key is adjacent to a comment inside the brackets, removing the brackets may detach or lose the comment during AST rewrite. |
| r2-p005 | fix ok | Static block interaction with computed key rewrite | class fields, static blocks, private names, super calls |
| r2-p006 | silent | Exception thrown during evaluation of computed key expression | exceptions thrown inside the code the fix removes or rewrites |
| r2-p007 | fix ok | Property order affects enumeration in legacy engines | If the object literal is used in a context where property order matters for iteration (e.g., legacy engines), changing from computed to literal key could alter enumeration order if the literal key sor |
| r2-p008 | fix ok | ASI hazard: leading parenthesis after object literal without semicolon | ASI and semicolon-less code, leading ( or [ on the next line |
Sources
r1-p001 · Escape sequences in computed keys (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-21-2)
1const o = {'a
2b': undefined}; console.log(o.hasOwnProperty('a
3b'));
r1-p002 · Numeric keys in destructuring patterns without numeric property support
1try { ({0: x} = {0: 42}); console.log(x); } catch (e) { console.log(e.name + ': ' + e.message); }
42
r1-p003 · Function declarations inside blocks, switch cases, labeled statements
1switch(1){case 1:{function f(){return{'a':1}};console.log(f().a);}break;}
1
r1-p004 · String vs number key distinction in typeof checks
1const o = {'0': undefined}; for (let k in o) { console.log(typeof k === 'string' ? 'str' : 'num'); break; }
str
r1-p005 · Computed __proto__ key altering prototype
1const o = {'__proto__': {toString(){return 'changed'}}}, p = {}; Object.assign(p, o); console.log(p.toString());
[object Object]
r1-p006 · Label/break/continue control flow with computed keys
1outer: for (;;) { inner: { if (true) break outer; } ({['a']: x} = {a: 1}); console.log(x); break; }
--- orig.js 2026-10-22 14:21:31.215257408 -0400 +++ fixed.js 2026-10-22 14:21:34.988415139 -0400 @@ -1 +1 @@ -outer: for (;;) { inner: { if (true) break outer; } ({['a']: x} = {a: 1}); console.log(x); break; } +outer: for (;;) { inner: { if (true) break outer; } ({'a': x} = {a: 1}); console.log(x); break; }
r1-p007 · Temporal dead zone after hoisting/reordering
1let x; ({['a']: x} = {a: 1}); console.log(x); let y = 2;
--- orig.js 2026-10-22 14:21:31.236258286 -0400 +++ fixed.js 2026-10-22 14:21:33.828366619 -0400 @@ -1 +1 @@ -let x; ({['a']: x} = {a: 1}); console.log(x); let y = 2; +let x; ({'a': x} = {a: 1}); console.log(x); let y = 2;
1
r1-p008 · Comments attached to removed/moved code
1/* keep */ const o = {['a'] /* comment */: 1}; console.log(o.a);
--- orig.js 2026-10-22 14:21:34.879410580 -0400 +++ fixed.js 2026-10-22 14:21:38.129546643 -0400 @@ -1 +1 @@ -/* keep */ const o = {['a'] /* comment */: 1}; console.log(o.a); +/* keep */ const o = {'a' /* comment */: 1}; console.log(o.a);
1
r2-p001 · Class field named 'constructor' conflicts with actual constructor
1class C {
2 ["constructor"] = () => {
3 console.log('field called');
4 return 'field';
5 };
6 constructor() {
7 this["constructor"](); // calls the field method
8 console.log(this.constructor === C); // should be true
9 }
10}
11const c = new C();
field called false
r2-p002 · Temporal dead zone after hoisting/reordering in destructuring
1let x;
2({[0]: x} = [99]);
3console.log(x);
4x = 42;
--- orig.js 2026-10-22 14:21:43.883787991 -0400 +++ fixed.js 2026-10-22 14:21:46.497897823 -0400 @@ -1,4 +1,4 @@ let x; -({[0]: x} = [99]); +({0: x} = [99]); console.log(x); x = 42;
99
r2-p003 · Var hoisting and redeclaration inside loop after use
1for (let i = 0; i < 2; i++) {
2 ({[i]: v} = [{}, {v: 1}]);
3 console.log(v?.v ?? 0);
4 var v = 99;
5}
0 1
r2-p004 · Comment inside brackets detached during bracket removal
1// before
2const o = {[/* keep */'a']: 1};
3// after
4console.log(o.a);
1
r2-p005 · Static block interaction with computed key rewrite
1class Base {
2 static ["staticProp"] = "base";
3}
4class Derived extends Base {
5 static {
6 console.log(this["staticProp"]); // reads rewritten literal
7 }
8 static ["staticProp"] = "derived";
9}
--- orig.js 2026-10-22 14:21:43.884788033 -0400 +++ fixed.js 2026-10-22 14:21:46.554900219 -0400 @@ -1,9 +1,9 @@ class Base { - static ["staticProp"] = "base"; + static "staticProp" = "base"; } class Derived extends Base { static { console.log(this["staticProp"]); // reads rewritten literal } - static ["staticProp"] = "derived"; + static "staticProp" = "derived"; }
base
r2-p006 · Exception thrown during evaluation of computed key expression
1try {
2 const obj = {["throw" + "key"]: 1};
3} catch (e) {
4 console.log(e.name, e.message);
5}
r2-p007 · Property order affects enumeration in legacy engines
1const o = {
2 ["2"]: "two",
3 ["1"]: "one"
4};
5let s = '';
6for (const k in o) s += k;
7console.log(s); // expects '21' if computed, '12' if literal due to numeric key ordering
--- orig.js 2026-10-22 14:21:46.365892274 -0400 +++ fixed.js 2026-10-22 14:21:48.453980082 -0400 @@ -1,6 +1,6 @@ const o = { - ["2"]: "two", - ["1"]: "one" + "2": "two", + "1": "one" }; let s = ''; for (const k in o) s += k;
12
r2-p008 · ASI hazard: leading parenthesis after object literal without semicolon
1let obj = {
2 ["x"]: 1
3}
4(function(){ console.log(obj.x); })()
--- orig.js 2026-10-22 14:21:46.400893745 -0400 +++ fixed.js 2026-10-22 14:21:50.166052132 -0400 @@ -1,4 +1,4 @@ let obj = { - ["x"]: 1 + "x": 1 } (function(){ console.log(obj.x); })()