eslint prefer-arrow-callback
labelled safe
Analysis Nemotron 3 Super
Flags function expressions used as callbacks or function arguments that could be replaced by arrow functions without changing behavior.
Fix: Replaces the function expression with an arrow function having the same parameters and body, preserving implicit returns and removing braces for single-expression bodies where applicable.
12 hazards
- Rewriting a function expression containing 'arguments' changes its meaning because arrow functions do not have their own arguments object.
- If the function expression uses 'yield', replacing it with an arrow function breaks generator semantics unless wrapped appropriately.
- Moving a function expression inside a conditional or loop via hoisting differences may alter execution timing if the original relied on function declaration hoisting.
- If the function expression is named and referenced recursively within its body, the arrow function rewrite removes that name, breaking recursion.
- Comments attached to the function keyword or braces may be lost or misplaced during the rewrite, affecting readability or tooling dependencies.
- In strict mode, the 'this' value inside the original function may be undefined, but the arrow function captures 'this' from outer scope, altering behavior if the outer 'this' differs.
- If the function expression is used as a constructor (e.g., new fn()), replacing it with an arrow function throws because arrow functions are not callable with new.
- When the function expression has default or rest parameters, the rewrite must preserve them exactly; incorrect transformation could alter parameter handling.
- If the function expression is part of a larger expression where its syntactic form affects precedence (rare), the arrow function’s tighter binding could change evaluation order.
- In TypeScript, if the function expression has a type assertion or generic constraint tied to the function keyword, removing it may break type checking.
- If the function expression is used in a context where its prototype property is accessed (e.g., fn.prototype), the arrow function lacks a prototype property, causing runtime errors.
- If the function expression contains a 'return' statement that exits an outer function, the arrow function’s implicit return or altered control flow may not replicate the same early exit behavior.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | silent prediction off | Prototype Property Access | Arrow function replacement removes .prototype property, breaking code that expects it. |
| r1-p002 | silent prediction off | Precedence Change via Binding Tightness | Arrow function binds tighter than function expression, altering evaluation order in mixed expressions. |
| r1-p003 | invalid | Class Fields / Private Names / Super Call | Replacing method expression breaks private field/super access due to lexical this capture. |
| r1-p004 | silent prediction off | Getter/Setter/DefineProperty Side Effects | Arrow function replacement loses identity, breaking defineProperty configurability/watch. |
| r1-p005 | silent prediction off | This Binding / Method Extraction / Call/Apply/Bind | Arrow function captures outer this, breaking method borrowing and explicit binding. |
| r1-p006 | silent prediction off | Constructor Usage | Arrow function replacement makes 'new fn()' throw, as arrow functions aren't constructible. |
| r1-p007 | invalid | Name Collisions Across Scopes After Move | Moving arrow function into inner scope causes accidental shadowing of outer variable. |
| r1-p008 | silent prediction off | Var Hoisting and Re-declaration in Loops/After Use | Replacing var function with arrow disrupts hoisting, causing TDZ if re-declared after use. |
| r2-p001 | fix ok | Early return from outer function | If the function expression contains a 'return' statement that exits an outer function, the arrow function’s implicit return or altered control flow may not replicate the same early exit behavior. |
| r2-p002 | silent prediction off | Precedence change via binding tightness | If the function expression is part of a larger expression where its syntactic form affects precedence (rare), the arrow function’s tighter binding could change evaluation order. |
| r2-p003 | fix ok | TypeScript type assertion/generic constraint | In TypeScript, if the function expression has a type assertion or generic constraint tied to the function keyword, removing it may break type checking. |
| r2-p004 | silent prediction off | null/undefined distinction, optional chaining short-circuit | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
| r2-p005 | silent prediction off | Generator function with yield | If the function expression uses 'yield', replacing it with an arrow function breaks generator semantics unless wrapped appropriately. |
| r2-p006 | silent prediction off | Numeric/string coercion: + vs template literal | numeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt |
| r2-p007 | silent prediction off | this binding: method extraction and call/apply/bind | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
| r2-p008 | silent | Function declaration inside block/switch/labeled | function declarations inside blocks, switch cases, labeled statements |
Sources
r1-p001 · Prototype Property Access
1function factory(){return function(){}};
2let F = factory();
3console.log(F.prototype !== undefined ? 'has' : 'no-proto');
has
r1-p002 · Precedence Change via Binding Tightness
1let x=1,y=2,z=0;
2x + y * z === 1 ? console.log('A') : console.log('B',x+y*z);
3(function(){z=x+y;})();
4console.log(z);
A 3
r1-p003 · Class Fields / Private Names / Super Call (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-25-0)
1class Base{constructor(){this.pub=1;}getPub(){return this.pub;}}
2class Der extends Base{constructor(){super();this.#priv=2;}
3 get(){return this.#priv + super.getPub();}}
4#DerPrv;let d=new Der();console.log(d.get());
r1-p004 · Getter/Setter/DefineProperty Side Effects
1let o={},id=null;function log(){id=this;return 42;}
2Object.defineProperty(o,'val',{get:log,configurable:true});
3console.log(o.val,id===o?'bound':'lost');
4delete o.val;Object.defineProperty(o,'val',{get:log});
5console.log(o.val,id===o?'bound':'lost');
42 bound 42 bound
r1-p005 · This Binding / Method Extraction / Call/Apply/Bind
1const obj={value:42,getValue:function(){return this.value;}};
2const fn=obj.getValue;
3console.log('direct:',obj.getValue());
4console.log('call:',fn.call(obj));
5console.log('bind:',fn.bind(obj)());
direct: 42 call: 42 bind: 42
r1-p006 · Constructor Usage
1function makeCtor(){return function Custom(){this.x=1;}};
2let C=makeCtor();try{let i=new C();console.log('ok',i.x);}catch(e){console.log(e.name,e.message);}
ok 1
r1-p007 · Name Collisions Across Scopes After Move (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-22T18-25-0)
1let x=9;(function(){let x=5;[1,2].map(v=>{console.log('inner',x+v);return x+v;}))();
2console.log('outer',x);
r1-p008 · Var Hoisting and Re-declaration in Loops/After Use
1(function(){console.log(typeof f);var f=function(){return 1};console.log(f());})();
undefined 1
r2-p001 · Early return from outer function
1function outer() {
2 [1,2].map(function(v) { if (v === 2) return 'found'; });
3 return 'not found';
4}
5console.log(outer());
--- orig.js 2026-10-22 14:25:25.586418929 -0400 +++ fixed.js 2026-10-22 14:25:28.766560568 -0400 @@ -1,5 +1,5 @@ function outer() { - [1,2].map(function(v) { if (v === 2) return 'found'; }); + [1,2].map((v) => { if (v === 2) return 'found'; }); return 'not found'; } console.log(outer());
not found
r2-p002 · Precedence change via binding tightness
1let result;
2try {
3 result = 0 + function() { return 1; }();
4} catch (e) {
5 result = e;
6}
7console.log(result);
1
r2-p003 · TypeScript type assertion/generic constraint
1// @ts-ignore
2const x = [];
3x.map(function(item) { return item.length; });
4console.log(x.map);
--- orig.js 2026-10-22 14:25:25.588419018 -0400 +++ fixed.js 2026-10-22 14:25:28.775560969 -0400 @@ -1,4 +1,4 @@ // @ts-ignore const x = []; -x.map(function(item) { return item.length; }); +x.map((item) => { return item.length; }); console.log(x.map);
[Function: map]
r2-p004 · null/undefined distinction, optional chaining short-circuit
1const obj = { prop: null };
2let val = obj.prop != null ? obj.prop() : 10;
3console.log(val);
4val = obj.prop == null ? obj.prop() : 10;
5console.log(val);
10
r2-p005 · Generator function with yield
1function* gen() {
2 yield [1,2].map(function*(v) { yield v * 2; });
3}
4const g = gen();
5console.log(g.next().value);
[ Object [Generator] {}, Object [Generator] {} ]r2-p006 · Numeric/string coercion: + vs template literal
1const obj = { valueOf: () => 5, toString: () => '5' };
2console.log(+obj);
3console.log(`${obj}`);
5 5
r2-p007 · this binding: method extraction and call/apply/bind
1const o = { x: 42, getX: function() { return this.x; } };
2const f = o.getX;
3console.log(f.call(o));
4console.log(f());
42 undefined
r2-p008 · Function declaration inside block/switch/labeled
1switch (1) {
2 case 1:
3 function f() { return 1; }
4 break;
5}
6console.log(typeof f);
function