transform-object-rest-spread: loose mode drops computed-key assignment when excluded binding is unused
Disclosure: This issue description was drafted with AI assistance and reviewed by me.
Input
function omit(obj, spec) {
const { [spec.key]: removed, ...rest } = obj;
return rest;
}
omit({ a: 1, b: 2 }, { key: "a" });Configuration
Reproduces with @babel/[email protected] and @babel/[email protected]:
plugins: [[require("@babel/plugin-transform-object-rest-spread"), {
loose: true,
useBuiltIns: true,
}]]It also reproduces via babel-preset-expo on web; original downstream report: expo/expo#49232.
Actual output
function omit(obj, spec) {
var _spec$key;
const rest = _objectWithoutPropertiesLoose(
obj,
[_spec$key].map(_toPropertyKey)
);
return rest;
}_spec$key is declared but never assigned, so the rest helper excludes undefined and returns { a: 1, b: 2 }.
Expected behavior
The computed key must remain assigned before it is used by the exclusion list, so the result is { b: 2 }.
When the removed binding is referenced, Babel emits the necessary inline memoization:
const { [_spec$key = spec.key]: removed } = obj,
rest = _objectWithoutPropertiesLoose(obj, [_spec$key].map(_toPropertyKey));Root cause
With loose / pureGetters optimization, removeUnusedExcludedKeys() removes the computed ObjectProperty because removed has zero references. The computed key assignment inserted for the rest exclusion is inside that property and is removed with it, but createObjectRest() has already emitted the temp identifier in the exclusion array.
A narrow fix should preserve a zero-reference computed property when its key contains the generated memoizing assignment (or otherwise preserve that assignment) while retaining unused-key removal for ordinary static keys.
Environment
- macOS
- Node 26.5.0
- @babel/core 7.29.7
- @babel/plugin-transform-object-rest-spread 7.29.7
Source: babel/babel