Background
It's typical to run into a situation where you want to get several values from an object or array. Rather than breaking each value retrieval up separately, destructuring solved that issue and allowed developers to start retrieving all desired values in a single declaration.
Traditional destructuring looks something like this:
Objects
const obj = { a: 1, b: 2, c: 3 };
const { a, b, c, d = b + c } = obj;
console.log(a, b, c, d) // -> 1, 2, 3, 5
Arrays
const arr = [1, 2, 3];
const [a, b, c, d = b + c] = arr;
console.log(a, b, c, d) // -> 1, 2, 3, 5
Explanation of the problem
However, there is no equivalent method for destructuring functional return values from a single value. For example, suppose I am developing a chess set. I might need several pieces of data about one chess piece in particular, and some of that data—rather than existing as properties on the node itself—is computed by passing the node into a function like this:
const chessPiece = document.querySelector('.chess-piece');
const key = getPieceKey(chessPiece),
alg = getElemAlg(chessPiece),
pos = getElemPos(chessPiece),
cid = `${key}${alg}`;
Proposal
I'm not suggesting a third destructuring syntax separate from the existing Object/Array destructuring syntaxes. Instead, I am proposing a new syntax that would work within either. This is what that could look like, similar to the computed property keys syntax.
Using the same example from above, the new syntax could look something like this:
const chessPiece = document.querySelector('.chess-piece');
const {
[getPieceKey]: key,
[getElemAlg]: alg,
[getElemPos]: pos,
cid = `${key}${alg}`
} = chessPiece;
Candidly, where I thought this might get trickier was Array destructuring, as the typical nested array destructuring syntax is nearly identical to this (see the next example below). However, if we include the same computed property keys-style syntax mentioned above, we avoid any reserved syntax conflict since the value is only being stored into a single variable name, similar to other destructured array values. Admittedly, this deviates more from traditional Array syntax, but it only adds one layer which only applies to destructuring, adds great value, and doesn't break any existing syntax.
const doubleFirstValue = ([firstValue]) => firstValue * 2;
const arr = [1, [2], [[3]]];
const [a, [b], [[c]], [doubleFirstValue]: aDoubled] = arr;
console.log(a, b, c, aDoubled) // -> 1, 2, 3, 2
The above example demonstrates both Array destructuring and Functional destructuring. Using this syntax likely wouldn't be as useful for Array destructuring compared to Object destructuring, but the example helps to illustrate that this would not cause any conflicts or be a breaking change to the existing syntaxes in place.
The same example could be used with Object destructuring syntax—which works more flexibly though less concisely—if a developer wanted to destructure without respect to the original insertion order of the array:
const doubleFirstValue = ([firstValue]) => firstValue * 2;
const arr = [1, [2], [[3]]];
const {
0: a,
[doubleFirstValue]: aDoubled,
1: { 0: b },
2: { 0: { 0: c } },
} = arr;
console.log(a, b, c, aDoubled) // -> 1, 2, 3, 2
Again, not a typical use case, but this works to demonstrate how flexible this could work for functionally destructuring from single values, arrays, or objects.
It would likely be more common to see examples like this crudely simple one:
const base = 5;
const {
[d => d * 2]: baseTimes2,
[d => d * 5]: baseTimes5,
[d => d * 10]: baseTimes10,
} = arr;
console.log(base, baseTimes2, baseTimes5, baseTimes10) // -> 5, 10, 25, 50
So far, this syntax seems to work across all cases I've considered, but I'd like to ask this community what a better syntax might be for this. What do you all think?
Workaround-ish…?
The closest I can seem to get this today without the proposed functional destructuring is to employ a helper function on the right-hand side of the destructuring statement, like this:
const helperFn = (value, functions) => (
functions.map((fn, i) => (
typeof fn === 'function' ? fn(value) : value[i]
))
);
const chessPiece = document.querySelector('.chess-piece');
const [key, alg, pos, cid = `${key}${alg}`] = helperFn(chessPiece, [getPieceKey, getElemAlg, getElemPos]);
However, using a helper function this way may prove less coherent and more complicated than using separate declarations, so I don't think it is a proper workaround. I've certainly come across this need enough times to warrant my attention. Adding a new syntax like the one I've suggested above could significantly help improve the overall destructuring DX, and I would love to discuss it further.
Open to any constructive feedback here. Thanks!