What Is Destructuring in JavaScript? (Objects and Arrays)
Learn javascript destructuring for objects and arrays default values, renaming, nested patterns, rest syntax, and how to type it all in TypeScript.
What Is Destructuring in JavaScript? (Objects and Arrays)#
I remember the first time I saw destructuring in a codebase. It was 2016, our team had just started using ES6 features, and a senior dev on the team wrote something like const { name, age } = user in a pull request. I stared at it for a good thirty seconds before I understood what was happening.
That moment stuck with me, because javascript destructuring is one of those features that looks alien for about five minutes and then you can't imagine writing code without it. It's sort of like learning keyboard shortcuts. Annoying at first, then indispensable.
If you've been avoiding destructuring or only using it in the most basic way, this post will walk you through everything: objects, arrays, defaults, renaming, nesting, rest syntax, function parameters, and how to add TypeScript types to all of it.
Object Destructuring The One You'll Use Every Day#
Object destructuring lets you pull properties out of an object and assign them to variables in a single line. Instead of this:
javascriptconst user = { name: "Sarah", age: 28, role: "engineer" };// The old wayconst name = user.name;const age = user.age;const role = user.role;
You write this:
javascriptconst user = { name: "Sarah", age: 28, role: "engineer" };// Destructuredconst { name, age, role } = user;console.log(name); // "Sarah"console.log(age); // 28console.log(role); // "engineer"
That's it. The curly braces on the left side aren't creating an object they're unpacking one. The variable names have to match the property names in the object (unless you rename them, which we'll get to).
And here's the thing you don't have to pull out every property. Grab what you need, leave the rest:
javascriptconst { name } = user;// age and role are just ignored
I use this constantly with API responses. You get back some massive object from a fetch call, and you only care about two or three fields. Destructuring makes that clean.
Renaming Variables#
Sometimes the property name in the object isn't what you want your variable to be called. Maybe it clashes with another variable, or maybe the API uses snake_case and you prefer camelCase. You can rename during destructuring:
javascriptconst apiResponse = { user_name: "Carlos", user_id: 42 };const { user_name: userName, user_id: userId } = apiResponse;console.log(userName); // "Carlos"console.log(userId); // 42// user_name and user_id are NOT defined as variables
The syntax is originalName: newName. I'll admit it's a bit counterintuitive it looks like you're creating an object with key-value pairs, but you're actually renaming. You get used to it.
Default Values#
What happens if a property doesn't exist on the object? Without a default, you get undefined. But you can set fallbacks:
javascriptconst config = { theme: "dark" };const { theme, language = "en", fontSize = 14 } = config;console.log(theme); // "dark" exists, so default is ignoredconsole.log(language); // "en" didn't exist, default kicks inconsole.log(fontSize); // 14
This is a pattern I use all the time for configuration objects. A function takes an options object, and you want sensible defaults for anything the caller didn't provide. Way cleaner than writing const language = config.language || "en" especially because the || approach has that annoying bug where 0 or "" get treated as falsy.
Tip: You can combine renaming and defaults:
const { user_name: userName = "Anonymous" } = data;
Array Destructuring#
Array destructuring works on the same principle, but uses position instead of property names:
javascriptconst colors = ["red", "green", "blue"];const [first, second, third] = colors;console.log(first); // "red"console.log(second); // "green"console.log(third); // "blue"
Square brackets on the left, and variables are assigned based on index. Want to skip an element? Just leave a gap with a comma:
javascriptconst [, , third] = colors;console.log(third); // "blue"
This is particularly handy with React hooks. Every time you write const [count, setCount] = useState(0), that's array destructuring. And if useState returned an object instead, you'd have naming collisions every time you used multiple state variables which is exactly why the React team chose array return values.
Swapping Variables#
One neat trick you can swap two variables without a temporary variable:
javascriptlet a = 1;let b = 2;[a, b] = [b, a];console.log(a); // 2console.log(b); // 1
Is this something you'll use every day? Probably not. But it's kind of satisfying when you do need it.
JavaScript Destructuring Syntax at a Glance#
Here's a quick reference for all the patterns:
| Pattern | Syntax | Example |
|---|---|---|
| Basic object | const { a, b } = obj | const { name, age } = user |
| Rename | const { a: x } = obj | const { name: userName } = user |
| Default value | const { a = 10 } = obj | const { port = 3000 } = config |
| Rename + default | const { a: x = 10 } = obj | const { name: n = "?" } = user |
| Basic array | const [a, b] = arr | const [first, second] = items |
| Skip elements | const [, , c] = arr | const [, , third] = colors |
| Rest (object) | const { a, ...rest } = obj | const { id, ...data } = user |
| Rest (array) | const [a, ...rest] = arr | const [head, ...tail] = list |
| Nested object | const { a: { b } } = obj | const { address: { city } } = user |
| Function param | function f({ a, b }) | function greet({ name, age }) |
Nested Destructuring#
Real-world data is rarely flat. API responses have objects inside objects inside arrays. Destructuring can handle that:
javascriptconst employee = { name: "Priya", department: { name: "Engineering", floor: 3, lead: { name: "Marcus", tenure: 5 } }};const { name: employeeName, department: { name: departmentName, lead: { name: leadName } }} = employee;console.log(employeeName); // "Priya"console.log(departmentName); // "Engineering"console.log(leadName); // "Marcus"
A word of caution though if you go more than two levels deep, the destructuring starts to become harder to read than just accessing properties with dot notation. I've seen code with four levels of nested destructuring and honestly, at that point, just use employee.department.lead.name. Readability matters more than cleverness.
Warning: Nested destructuring will throw a TypeError if an intermediate object is
undefinedornull. Consider using optional chaining for potentially missing nested data.
Rest Syntax Grab Everything Else#
The rest pattern (...) collects whatever properties you didn't explicitly destructure into a new object (or array):
javascriptconst user = { id: 1, name: "Alex", email: "alex@dev.io", role: "admin" };const { id, ...profileData } = user;console.log(id); // 1console.log(profileData); // { name: "Alex", email: "alex@dev.io", role: "admin" }
This is incredibly useful when you need to separate a property from the rest like stripping id before sending data to an API, or removing sensitive fields before logging. I use this pattern almost daily.
It works the same way with arrays:
javascriptconst [winner, ...losers] = ["Alice", "Bob", "Charlie", "Dana"];console.log(winner); // "Alice"console.log(losers); // ["Bob", "Charlie", "Dana"]
Destructuring in Function Parameters#
Try the JS to TypeScript
Paste your code, get the result instantly. AI-powered, free, no signup.
Open JS to TypeScriptThis might be my favorite use case. Instead of passing an object and then destructuring it inside the function body, you can destructure right in the parameter list:
javascript// Without destructuringfunction createUser(options) { const name = options.name; const age = options.age; const role = options.role || "viewer"; // ...}// With destructuring + defaultsfunction createUser({ name, age, role = "viewer" }) { console.log(`${name}, ${age}, ${role}`);}createUser({ name: "Jordan", age: 31 });// "Jordan, 31, viewer"
This pattern makes your function signatures self-documenting. Anyone reading the code can immediately see what properties the function expects. A team I worked with a couple years back had a rule if a function takes more than two parameters, use a destructured options object. Honestly, it made our codebase so much more readable.
Adding TypeScript Types to Destructured Variables#
If you're moving from JavaScript to TypeScript and if you're not already, you probably should be you'll need to know how to type destructured values. The syntax trips people up at first because the colon is already used for renaming.
Here's how it works:
typescript// Type the whole object being destructuredconst { name, age }: { name: string; age: number } = user;// In function parameters this is the clean wayfunction createUser({ name, age, role = "viewer" }: { name: string; age: number; role?: string;}) { // name is string, age is number, role is string}
But honestly, you almost never want to inline the type like that. Pull it out into an interface that's what TypeScript interfaces are for:
typescriptinterface UserOptions { name: string; age: number; role?: string;}function createUser({ name, age, role = "viewer" }: UserOptions) { console.log(`${name}, ${age}, ${role}`);}
Much cleaner. And if you've got existing JavaScript code with destructuring that you need to convert to TypeScript, SnipShift's JS to TypeScript converter↗ can handle the type inference automatically it'll figure out the types from context and generate proper interfaces rather than just typing everything as any.
If you're starting a bigger migration, our guide on converting JavaScript to TypeScript covers the full process.
Common Gotchas#
A few things that bite people:
Destructuring undefined or null throws. If you try const { name } = undefined, you get a TypeError. Always make sure the thing you're destructuring actually exists. Default parameters help here: function fn({ name } = {}).
You can't destructure into existing variables without parentheses. This fails:
javascriptlet name, age;// SyntaxError JS thinks { starts a block{ name, age } = user;// Fix: wrap in parentheses({ name, age } = user);
That parentheses requirement is something that still catches me off guard occasionally. But it only comes up when you're reassigning, not when you're declaring with const or let.
Deeply nested defaults are tricky. Something like const { a: { b = 5 } = {} } = obj works but gets confusing fast. If you need this level of complexity, consider just using a utility function or nullish coalescing after the fact.
When NOT to Destructure#
Not everything needs to be destructured. Here are cases where I'd skip it:
- You're only accessing one property once.
user.nameis simpler thanconst { name } = userif you only usenamein one spot. - The nested structure is deep and complex. Past two levels, dot notation with optional chaining reads better.
- You need the original object too. If you're going to use
useritself and also needuser.name, destructuring adds noise.
Destructuring is a tool, not a religion. Use it when it makes code clearer, skip it when it doesn't.
The Bottom Line#
JavaScript destructuring is one of those ES6 features that has genuinely made code better more readable, more concise, and more expressive. Once it clicks, you'll find yourself using it everywhere: function parameters, API responses, React hooks, config objects, the works.
Start with the basics pull a few properties out of an object. Then experiment with defaults and renaming. Then try nested patterns and rest syntax. And when you're ready to add TypeScript types on top, you've already got the hard part down.
If you want to see how your destructured JavaScript translates into properly typed TypeScript, give SnipShift's converter↗ a try paste your code and see the typed version instantly. And for more fundamentals like this, check out our post on the difference between JavaScript objects and JSON, which covers another concept that looks simple until you get bitten by the edge cases.
Happy destructuring. Or deconstructing. Or unpacking. Whatever you want to call it just don't call it "decomposing." That's for organic matter, not code.
Try the JS to TypeScript
Paste your code, get the result instantly. AI-powered, free, no signup.
Open JS to TypeScript