0

I was doing some destructuring in ES6 and ran into an unexpected situation. Here's a trivialized version of what happened.

let obj = {x: {y: 5}};
let {x: {y}} = obj;

console.log(x); // x is not defined
console.log(y); // 5

In my use case, I needed access to both x and y. I would have expected x to have been destructured as well. Instead, to get the desired effect, I had to do this:

let obj = {x: {y: 5}};
let {x, x: {y}} = obj;

console.log(x); // {"y":5}
console.log(y); // 5

However, I think {x, x: {y}} looks weird and unintuitive. Is there a destructuring secret I'm not aware of, or is this just a tiny destructuring pitfall?

  • As for intuitiveness of your expectations, how would you expect `const {x: {x}} = {x: {x: 5}}` to behave? Should it destructure the first `x` and then reassign `x` to the second `x`? But `x` is a constant and can't be reassigned. Should it throw `x has already been declared` then? – Estus Flask Jul 29 '16 at 21:33

2 Answers2

1

This:

let {x: {y}} = obj;

is an abbreviation for:

let {x: {y: y}} = obj;

Destructuring matches the colons’ left-hand sides (the keys) against the data and puts the results into the right-hand sides (assignment targets, usually variables). Therefore, x is just a key here, while y is both a key and a newly declared variable.

Axel Rauschmayer
  • 25,381
  • 5
  • 24
  • 17
  • Thanks, @AxelRauschmayer, nice to have an answer from you here. Sounds like simple but intelligible explanation to me. – Estus Flask Jul 30 '16 at 01:33
0

It isn't a pitfall but the expected behaviour, something that is defined by destructuring syntax.

There may be AssignmentPropertyList in ObjectAssignmentPattern and nested AssignmentPropertyList in AssignmentPropertyList. Each AssignmentPropertyList can be either AssignmentProperty or PropertyName : AssignmentElement.

In this case

let {x, x: {y}} = obj;

the first x is AssignmentProperty, the second x is PropertyName.

The idea behind nested destructuring is to provide a path for nested property that will be destructured. The expected behaviour is to not get unwanted variables for each level of nested destructuring, intermediate x should be destructured explicitly if necessary.

There should be nothing weird or nonintuitive in this syntax for the developer who is familiar with it. The variation that is closer to what the code does (and possibly more readable) is

let {x} = obj;
let {y} = x;
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks for insulting me along the way! –  Jul 29 '16 at 21:32
  • @BlueJ774 I didn't have an insult in mind. But glad I could help any way. – Estus Flask Jul 29 '16 at 21:35
  • "There should be nothing weird or nonintuitive in this syntax for the developer who is familiar with it." I'm just saying, this surprised me and a second developer. We've both been writing enterprise ES6 code for two years. –  Jul 29 '16 at 21:40
  • 1
    @BlueJ774 Best to think of destructuring kind of like the opposite of creating an object, where `let y = 5; let obj = {x: {y}};` doesn't try to read an `x` to get the key, destructuring doesn't try to write to an `x` variable. – loganfsmyth Jul 29 '16 at 21:47
  • 1
    There's no humiliation in this sentence, it is about degree of familiarity. I wasn't very fond of how unreadable the code becomes with some ES6+ features a year ago, now I'm totally ok with how they look there. As for destructuring, I'm not confused with `let {x, x: {y}}` but still prefer `let {x} = obj; let {y} = x`, it just looks right. – Estus Flask Jul 29 '16 at 21:54
  • 1
    I'm sorry. I misunderstood your intention with the sentence. –  Jul 29 '16 at 22:04
  • @BlueJ774 No problem. – Estus Flask Jul 30 '16 at 01:26