Dan Levy's Avatar DanLevy.net

Quiz: Destructuring Delights

Are you a maestro of Destructuring?

Quiz: Destructuring Delights

Or is it your Symphony of Destruction?

This quiz will test your knowledge of Destructuring in JavaScript: from “basic” object syntax to nested destructuring and default values. Plus bonus questions on TypeScript and inline types!

Jump right in to the warmup - prove your Destructuring skills! 👇

1. 

Warmup: Objects

What will this code print?

const person = {
name: 'Dan Levy',
location: 'Cape Town',
};
const { name, age } = person;
console.log(`Name: ${name}, Age: ${age}`);

The age property does not exist on person, so age will be undefined. Definitely not Infinity 😅

This results in:

Name: Dan Levy, Age: undefined

2. 

Warmup: Arrays

What will this code do?

const person = [ 'Dan Levy', 'Cape Town' ];
const [ name, origin, age ] = person;
console.log(`Name: ${name}, Age: ${age}`);

The age variable is not present in the tuple array, so it will be undefined.

This results in:

Name: Dan Levy, Age: undefined

3. 

Nested Destructuring

How about some nested destructuring?

'use strict';
const person = {
name: { first: 'Dan' },
address: { city: 'Denver' },
};
const {
name: { first },
address: { city },
birth: { place },
} = person;
console.log(
`First: ${first}, City: ${place}`,
);

The birth: { place } property does not exist on person, so it will throw an error. One solution is to provide default values for nested properties.

When accessing nested properties - be careful - as the errors can be hard to spot. And error messages vary between browsers and other platforms, making it a bit more challenging to debug.

In modern Chrome: TypeError: Cannot read properties of undefined (reading 'place')

In Node 20, ReferenceError: place is not defined

In Safari and Bun, TypeError: Right side of assignment cannot be destructured

4. 

Defaults

Now with some defaults, what will this do?

'use strict';
const person = {
name: { first: 'Dan' },
address: { city: 'Denver' },
};
const {
name: { first = 'Unknown' },
birth: { place = 'Unknown' },
} = person;
console.log(
`Hi ${first} from ${place}`,
);

The birth property does not exist on person, so the whole object still needs a default, not just the nested property. Basically it’s missing a = {} default in there.

The way this is written, says “if person.birth is undefined, then place is Unknown”. But person.birth is undefined, so it’s trying to destructure undefined, which results in an error.

In modern Chrome: `TypeError: Cannot read properties of undefined (reading 'place')`
In Node 20, `ReferenceError: place is not defined`
In Safari and Bun, `TypeError: Right side of assignment cannot be destructured`

5. 

Defaults

What will this do?

const person = {
name: { first: 'Dan' },
address: { city: 'Denver' },
};
const {
name: { first = 'Unknown' },
birth: { place = 'Unknown' } = {},
} = person;
console.log(
`Hi ${first} from ${place}`,
);

The birth property does not exist on person, so it falls back to an empty object = {}. This allows the default value to be used.

6. 

Function Arguments

Now as function parameters, what will this do?

'use strict';
function displayUser({
name = "Unknown",
age = -1,
} = { place: "Unknown" }) {
console.log(`Hi ${name} from ${place}`);
}
displayUser({ name: "Dan" });

This function extracts name and age properties, using defaults if necessary. In this case, the place key on the default object is just noise, it’s not used inside displayUser().

In Strict mode, this will throw an error because place is not defined in the function scope. In non-strict mode, it will print Hi Dan from Unknown.

7. 

Function Arguments

How are undefined values handled?

'use strict';
function displayPlace({
name = "N/A",
place = "N/A",
age = -1,
} = { place: "Unknown" }) {
console.log(`${place}`);
}
displayPlace({ name: "Dan" });
displayPlace({ name: "Dan", place: undefined });
displayPlace({ name: "Dan", place: "Joburg" });

The function displayPlace will ONLY use a default object if no object is passed in. So, the only way to get the { place: "Unknown" } default is with zero arguments displayPlace().

Another notable behavior here is that passing undefined for place will cause the default value to be used, a bit similar to JSON.stringify behavior (ignoring undefined, recognizing null).

This results in:

displayPlace() // Unknown
displayPlace({ name: "Dan" }) // N/A
displayPlace({ name: "Dan", place: undefined }) // N/A

8. 

Function Arguments

Similar to the previous one… how is null handled?_

function displayPlace({
name = "N/A",
place = "N/A",
age = -1,
} = { place: "Unknown" }) {
console.log(`${place}`);
}
displayPlace({ name: "Dan", place: null });
displayPlace({ name: "Dan", place: undefined });

In this case, the place property is set to null in the first call, and undefined in the second. The default value for place is only used if the whole object is missing or undefined. Nulls will come through as null.

TypeScript Ahead

9. 

TypeScript Inline Types

Now in TypeScript… what will this do?

'use strict';
function displayPlace(
{
name = 'N/A',
place = 'N/A',
}: {
name: string;
place: string;
age: number;
},
) {
console.log(`${place}`);
}
displayPlace({ name: 'Dan', place: null });

TypeScript will throw an error because the place property cannot be null.

If you ignore type errors, running the code will print null to the console.

So there’s 2 answers.

10. 

TypeScript: With Assignment

Let’s try some renaming/assignment…

'use strict';
function displayPlace({
name = 'N/A',
place: location = 'N/A',
}: {
name: string;
place: string;
age?: number;
}) {
console.log(`${location}`);
}
displayPlace({ name: 'Dan', place: 'Denver' });

This will print Denver to the console. The place property is renamed to location in the function signature. This is a common pattern (rename properties during destructuring) when adapting 3rd party data structures.

11. 

Nested Destructuring in TS

Spot the type error:

function greet({
name: {first = "N/A", last = "N/A"},
birth: {place = "N/A"} = {},
age = -1,
}: {
name: {first?: string, last?: string};
birth: {place?: string};
age: number;
}) {
console.log(`Hi ${first} ${last} from ${place}`);
}
greet({ name: {first: 'Dan'} });

The error is in the greet function signature. The age and birth properties are missing in the passed object. so they should be optional in the type definition.

Even though the birth property is destructured with a default value, the type definition requires it to be present. In order to mark a property as optional in TypeScript, you should use the ? operator.

Note birth?: number isn’t the same as birth: number | undefined.

12. 

TypeScript + Assignment

Now with assignment (note the f, l and p variables)

'use strict';
function greet(
{
name: {first: f = "N/A", last: l = "N/A"},
birth: {place: p = "N/A"} = {},
age = -1,
}: {
name: {first?: string, last?: string};
birth?: {place?: string};
age?: number;
}
) {
console.log(`Hi ${f} ${l} from ${place}`);
// What will 👆 do?
}
greet({
name: {first: 'Dan', last: 'Levy'},
birth: {place: 'Cape Town'},
});

Another Error! You’re starting to guess aren’t you?!

It is hard to read layers of destructuring, with defaults, assignment and types!

As soon as place is reassigned to the p variable it is no longer defined in the scope of the console.log statement.

console.log(`Hi ${f} ${l} from ${place}`); // ❌
// to:
console.log(`Hi ${f} ${l} from ${p}`); // ✅

Yike’s! That was a lot of destructuring, defaults, nesting, assignment, and TypeScript! 🤯

I hope you enjoyed it!

If you’re looking for more… Of course you are! Check out my Quiz Collection for more thrills & excitement!

Quiz Score:

Congrats! Quiz completed.

Edit on GitHubGitHub