Files
oxc/crates
YTandqing deng e3b1bb4e44 fix(transformer/class-properties): keep the value of a private accessor assignment (#28716)
If the target is `es2021` or lower, `this.#x += k`, `this.#x ??= k`, and
`++this.#x` on a private accessor give the wrong value. They should give
the new value of `#x`, but they give the return value of the setter
instead. Most setters return nothing, so the value is usually
`undefined`.

```js
class Counter {
  #n = 1;
  get #count() { return this.#n; }
  set #count(v) { this.#n = v; }
  add(k) { return (this.#count += k); }
}
console.log(new Counter().add(2)); // 3
```

The transformed code prints `undefined`, because the result of the
expression is the setter call:

```js
// before
return _set_count.call(_assertClassBrand(_Counter_brand, this), _get_count.call(_assertClassBrand(_Counter_brand, this)) + k);

// after
return _toSetter(_set_count.bind(_assertClassBrand(_Counter_brand, this)))._ = _get_count.call(_assertClassBrand(_Counter_brand, this)) + k;
```

Plain `=` and postfix `x++` are already correct, and plain `=` already
uses `_toSetter`. This PR uses the same form if the value of the
expression is used. If the expression is a statement, the output does
not change.

I did not use a temp var, because a temp var in a default parameter is
shared between calls, and a recursive setter overwrites it.

The new fixture `oxc/private-accessor-assignment-value` covers instance
and static accessors, all the operators above, an object from a function
call, arrow function bodies, and the recursive case. Its `exec.js` fails
on main with `expected 'from setter' to be 3`.

---

AI assistance was used for this change. I reviewed and tested it.

---------

Co-authored-by: qing deng <dengqing0821@gmail.com>
2026-11-03 15:48:08 +08:00
..