2

I have the following object:

const action = {id: 100, name: 'Andres', age: 27, type: 'CREATE_USER'}

I want to copy some properties into another object. In latest version of Chrome I can do:

const newUser = {id, name, age} = action;

And the newUser object contains:

{id: 100, name: 'Andres', age: 27}

This works fine in the browser. However, with my compiled JS code it doesn't work. I get:

 error ReferenceError: id is not defined

If I remove id, I get the same message with different key and so on.

My setup:

"webpack": "2.1.0-beta.21"
"webpack-dev-server": "2.1.0-beta.0"

And for Babel:

"babel-core": "6.x",
"babel-eslint": "6.x",
"babel-loader": "6.x",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-runtime": "6.x",
"babel-polyfill": "^6.8.0",
"babel-preset-es2015": "6.x",
"babel-preset-es2015-native-modules": "^6.6.0",
"babel-preset-react": "6.x",
"babel-preset-stage-0": "6.x",

Is there something I'm missing with Babel or Webpack to accomplish what I want?

Filip Dupanović
  • 32,650
  • 13
  • 84
  • 114
Andres Zapata
  • 1,710
  • 1
  • 16
  • 32
  • Your webpack config could be wrong. Chances are that the linter is the one that is failing. – givanse Dec 24 '16 at 03:26
  • @givanse I don't have any linter config with webpack and I get the error in the browser console, after compile from webpack. – Andres Zapata Dec 24 '16 at 03:31
  • Did you look at the actual generated code in your JS file to see what the executing code actually is? – jfriend00 Dec 24 '16 at 03:42
  • @jfriend00 no I havent, will do – Andres Zapata Dec 24 '16 at 03:43
  • 2
    *"In latest version of chrome I can do"* That code certainly doesn't look valid. – Felix Kling Dec 24 '16 at 07:13
  • To correct my previous comment, it's basically the same as doing something like `var foo = bar = 42;`. So yes, it's syntactically valid, but certainly not doing what you think it does. – Felix Kling Dec 24 '16 at 16:04
  • Btw, what you are trying to achieve has been asked quite often, e.g. [Is it possible to destructure an object and generate a new object in a single statement?](http://stackoverflow.com/q/32102061/218196) – Felix Kling Dec 24 '16 at 16:07
  • @FelixKling does not exactly feel like a duplicate and the commentary on the question, http://stackoverflow.com/questions/35531775/javascript-object-destructuring-and-aliasing#comment58753018_35531775, seems to be the same problem the OP had here. It's just incidental the assignment does not give the results OP here expected, the main issue was that this doesn't work with Babel at all. Perhaps we can do something to improve the question? – Filip Dupanović Dec 25 '16 at 21:21
  • The answers (which are - NO, it's not possible) are now superceded. See babel-plugin-transform-object-spread which will allow for this. – Chanoch Jun 08 '18 at 10:19

2 Answers2

6

The problem here is that you're attempting to declare and assign to variables at once and this is not allowed under strict mode semantics, which is enforced by Babel. So, your snippet does more than you would expect:

 const newUser = {id, name, age} = action;

As part of evaluating the destructuring expression, an assignment will be performed to id, name, age, references which do not exist, which should throw a ReferenceError exception according to the specification. More specifically, there is a note at the bottom which says:

When an assignment occurs within strict mode code, it is an runtime error if lref in step 1.f of the first algorithm or step 7 of the second algorithm it is an unresolvable reference. If it is, a ReferenceError exception is thrown.

You can try running this snippet in your console to prove so:

// Will work
(function () {
  a = 5;
})();

// Won't work
(function () {
  'use strict';
  b = 5;
})()

To get this working with Babel, the way I think you'd want to, you would have to forgo the ES2015 preset and manually include all the plugins, opting out of the transform-strict-mode. In short, you will open a can of PITA on yourself.

The correct thing to do would be to declare all the variables for the properties you'd like to bind to:

'use strict';

let a, b, c;

({fee: a, fii: b, foo: c} = {fee: 1, fii: 2, foo: 3});
console.log(a, b, c);

Unfortunately, this will still not get you what you want. The result of destructuring expressions is an assignment expression where the returned value will be the value assigned (destructured), so your newUser will not actually be a new object as you expected:

const action = {id: 100, name: 'Andres', age: 27, type: 'CREATE_USER'}
const newUser = {id, name, age} = action;

console.log(newUser === action);

Which leads us to the following solution. It might not be as terse, but it clearly outlines the work being performed:

const action = {id: 100, name: 'Andres', age: 27, type: 'CREATE_USER'}
const {id, name, age} = action;

const newUser = {id, name, age};
Filip Dupanović
  • 32,650
  • 13
  • 84
  • 114
1
const action = {id: 100, name: 'Andres', age: 27, type: 'CREATE_USER'}    
const newUser = {id, name, age} = action;

And the newUser object contains:

{id: 100, name: 'Andres', age: 27}

No, it doesn't. It still contains the type property. The reason is that the second statement is parsed as:

const newUser = ({id, name, age} = action);

In other words, newUser is assigned to the result of evaluating the assignment statement ({id, name, age}) = action. An assignment statement evaluates to its right-hand side. In this case, it therefore evaluates to action. Overall, your assignment statement is exactly equivalent to the two following assignments:

({id, name, age} = action);
const newUser = action;

That's almost certainly not what you want. id etc. will be implicitly declared as global variables or more likely cause a ReferenceError. newUser will just be the same as action.

What you are trying to do is to deconstruct into another object, rather than into variables. There is no such feature in ES6. There have been some discussions and proposals, such as a "pick" feature, but the powers that be on TC39 seem decisively uninterested. For the time being, then, write

const {id, name, age} = action;
const newUser = {id, name, age};

If you have access to the spread properties features, and know you want everything except type, then you can write:

const {type, ...newUser} = actions;

but this is not really a good general solution to "picking".