Destructure a JavaScript object to existing variables
By Flavio Copes
Learn how to destructure an object into existing variables in JavaScript by wrapping the assignment in parentheses, plus the semicolon trick you need.
To destructure an object into variables you already declared, drop the const and wrap the assignment in parentheses:
let a, b
;({ a, b } = doSomething())
Let me show you where this comes up, and why the syntax looks so odd.
I had this problem. I was calling a function to get some data:
const doSomething = () => {
return { a: 1, b: 2 }
}
const { a, b } = doSomething()
but I had the need to wrap this into an if block, to only execute this line if the user is logged in. Moving the const declaration inside the if block made those variables invisible outside of that block.
So I wanted to declare those variables first, as undefined variables, and then update them when the data came in.
The first part is easy:
let a, b
Then inside the if block we assign to them with destructuring, wrapping the line in parentheses:
let a, b
const doSomething = () => {
return { a: 1, b: 2 }
}
if (/* my conditional */) {
({ a, b } = doSomething())
}
Why do we need the parentheses?
When a statement starts with {, JavaScript parses it as a code block, not as an object.
So without parentheses this line:
{ a, b } = doSomething()
fails with SyntaxError: Unexpected token '='. The engine sees a block containing a and b, then an = sign that makes no sense there.
Wrapping the line in parentheses turns it into an expression. Inside an expression, { a, b } is a valid destructuring pattern.
Array destructuring does not have this ambiguity, because a statement can’t start with a block when it starts with [. But as you’ll see next, it shares the same semicolon problem.
The semicolon trick
If you’re like me and you don’t like using semicolons, you need to add a semicolon before the line (and Prettier should automatically add it for you, too, if you use it):
let a, b
const doSomething = () => {
return { a: 1, b: 2 }
}
if (/* my conditional */) {
;({ a, b } = doSomething())
}
Here’s why. Without semicolons, JavaScript joins lines when it can. Take this code:
const total = 100
({ a, b } = doSomething())
The engine reads it as 100({ a, b } = doSomething()), a function call on the number 100. You get TypeError: 100 is not a function at runtime, and the error points at a line that looks perfectly fine.
The leading semicolon terminates the previous statement, so the parentheses start fresh.
This is the same reason we prepend a semicolon to an IIFE (immediately-invoked function expression):
;(() => {
//...
})()
It prevents JavaScript from merging lines that are not terminated by semicolons.
Related posts about js: