Problem
The JS emitter drops the grouping/call around an immediately invoked arrow expression. A value-producing arrow IIFE is emitted as an arrow whose body is called instead:
class P { x = 18; arrow() { return (() => this.x)(); } }
class F { #x = 18; arrow() { return (() => this.#x)(); } }
class A {
#v = 18;
get #x() { return this.#v; }
arrow() { return (() => this.#x)(); }
}
console.log(JSON.stringify([new P().arrow(), new F().arrow(), new A().arrow()]));Executed on current main (64d7b37b6) with Node v24.18.0 and a freshly built home-tool:
- Node:
[18,18,18] home-tool:[null,null,null](JSON.stringifyrenders the returned functions asnull)
home-tsc shows the structural defect directly. For the accessor case it emits:
arrow() {
return () => this.#x();
}The required form is return (() => this.#x)();. This is a general arrow/call precedence problem: public properties, private fields, and private accessors all reproduce it. It was found while validating zig-utils/zig-js#943 and is independent of that private-declaration fix.
Acceptance
- Parenthesized arrow expressions retain the grouping required when used as call/new/member operands.
- Immediate invocation returns the arrow body result for public, private-field, and private-accessor members.
- Nested/async/generic/JSX-shaped arrows and ordinary uncalled arrows preserve current output.
- Add focused emitter and end-to-end runtime cases; compare the emitted program and result with Node.
No-workaround boundary
Do not special-case private names or these fixture strings. Fix precedence/parenthesization at the arrow-expression call boundary.