Quiz: Destructuring Delights
Are you a maestro of Destructuring?
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! 👇
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
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
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, this is also a TypeError because JavaScript tries to destructure place from undefined before place is ever read.
Exact wording varies between browsers and runtimes.
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 is missing, so JavaScript tries to destructure place from undefined. This throws before the default for place can apply:
TypeError
The exact diagnostic message varies between runtimes. A property default only handles a missing or undefined property after its containing object is available. Use birth: { place = "Unknown" } = {} to supply a default object when birth itself is missing or undefined.
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.
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().
Strict mode doesn’t change this one: reading the undeclared place binding throws a ReferenceError.
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 default parameter object { place: "Unknown" } is used when the argument is omitted or explicitly undefined: both displayPlace() and displayPlace(undefined) print Unknown.
When an object is supplied, a missing or undefined place property uses the property default "N/A". These parameter and property defaults apply at different levels.
This results in:
displayPlace() // Unknown
displayPlace({ name: "Dan" }) // N/A
displayPlace({ name: "Dan", place: undefined }) // N/A
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 property default "N/A" applies when place is missing or undefined, even if the object itself is supplied. null does not trigger that default, so the outputs are null and N/A.
TypeScript Ahead
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 reports an error because place is typed as string, but the call passes null. The call also omits the required age property.
If you ignore type errors, running the code will print null to the console.
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.
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?: { place?: string } isn’t the same as birth: { place?: string } | undefined.
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!