We want to transpile this code, ensuring that every property is accessed the correct number of times and in the correct order:
var {
val_1_1: { val_2_1, ...val_2_rest },
val_1_2,
val_1_3: [
arr_1,
arr_2,
{ val_3_1, ...val_3_rest },
arr_4
],
val_1_4
} = foo();
Since the properties included in the rest object must be accessed after the propertie which come before the rest operator and before the properties which come after, we need to insert the transpiled declaration exactly where the rest operator is in the original code. This is easy when the rest element is the last of the destructuring pattern:
// (Assume that access to obj is known to be pure)
// Input
var { a, ...rest } = obj;
// Output
var { a } = obj,
rest = _objectWithoutProperties(obj, ["a"]);
If the rest element is inside a nested object pattern (but it still is the last element of the whole destructuring pattern), we need to ensure that the nested object is got exactly once:
// Input
var { a, b: { c, ...rest } } = obj;
// Current output ("b" is accessed twice)
var { a, b: { c } } = obj,
rest = _objectWithoutProperties(obj.b, ["c"]);
// Expected output
var { a, b: _b } = obj,
{ c } = _b,
rest = _objectWithoutProperties(_b, ["c"]);
What if theere is something after the rest element?
// Input
var { a, b: { ...rest }, c } = obj;
We can't just "extract" the { ...rest }
pattern after the variable declarator (like in the example before), because
the properties included in rest
must be accessed before c
. Thus, this is wrong:
var { a, b: _b, c } = obj,
rest = _objectWithoutProperties(_b, []);
We need to "split" the whole destructuring pattern, to make room for the _objectWithoutProperties
call:
// Partial step
var { a, b: { ...rest } } = obj,
{ c } = obj;
// Output
var { a, b: _b } = obj,
rest = _objectWithoutProperties(_b, []),
{ c } = obj;
We have only considered object destructuring, but there is also array destructuring. It is trickier to "split", since the elements are always accessed from the first to the last. It can't be split like this:
// Input
var [ a, { ...rest }, b ] = obj;
// Wrong partial step
var [ a, { ...rest } ] = obj,
[ b ] = obj;
The solution to this problem is to have an iterable which isn't reset every time that it is used. We need to introduce a
_stepsIterable
helper. This helper forwards next()
calls to the wrapped iterator, and calls return()
only when needed:
// Input
var [ a, { ...rest }, b ] = obj;
// Partial step
var _obj = _stepsIterable(obj),
[ a, { ...rest } ] = _obj,
[ b ] = _obj;
_obj.return();
// Output
var var _obj = _stepsIterable(obj),
[ a, _x1 ] = _obj,
rest = _objectWithoutProperties(_x1, []),
[ b ] = _obj;
_obj.return();
The _stepsIterable
function is defined like this:
function _stepsIterable(iterable) {
var iterator = iterable[Symbol.iterator]();
return {
[Symbol.iterator]: () => ({
next: (arg) => iterator.next(arg),
}),
return: () => iterator.return(),
};
}
Given all these considerations, the original example is transpiled to this:
// Input
var {
val_1_1: { val_2_1, ...val_2_rest },
val_1_2,
val_1_3: [
arr_1,
arr_2,
{ val_3_1, ...val_3_rest },
arr_4
],
val_1_4
} = foo();
// Partial step 1
var _foo = foo(),
{ val_1_1: _val_1_1 } = _foo,
{ val_2_1 } = _val_1_1,
val_2_rest = _objectWithoutProperties(_val_1_1, ["val_2_1"]),
{ val_1_2, val_1_3: _val_1_3 } = _foo,
_val_1_3_tmp = _stepsIterable(_val_1_3),
[ arr_1, arr_2, _x2 ] = _val_1_3,
{ val_3_1 } = _x2,
val_3_rest = _objectWithoutProperties(_x2, ["val_3_1"]),
[ arr_1 ] = _val_1_3_tmp;
_val_1_3_tmp.return();
var { val_1_4 } = _foo;
_stepsIterable can be implemented like this:
The last example would then become