RSS Amplifier

DanLevy.net · Nov 12, 2024

Quiz: Destructuring Delights

0
Sign in to vote or save

Dan Levy · danlevy.net

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

Name: Dan Levy, Age: 20

Name: Dan Levy, Age: 40

Name: Dan Levy, Age: Infinity

Name: Dan Levy, Age: undefined

Error: Cannot read property 'age'

Name: undefined, Age: 40

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

Name: Dan Levy, Age: NaN

Name: Dan Levy, Age: null

Name: Dan Levy, Age: undefined

Name: Dan Levy, Age: 40

Error: Cannot destructure property 'age'

SyntaxError: Unexpected token ','

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.

First: Dan, City: Denver

First: undefined, City: Denver

Error: Cannot read property 'first'

First: Dan, City: undefined

Error

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, this is also a `TypeError` because JavaScript tries to destructure `place` from `undefined`.

Exact wording varies between browsers and runtimes.

Hi Dan from Unknown

Hi Dan from Denver

Hi Unknown from Unknown

Hi Unknown from Denver

Error

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.

Hi Dan from Denver

Hi Dan from Johannesburg

Hi Dan from Unknown

Hi Unknown from Unknown

Hi Unknown from Denver

Error

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.

Hi Dan from undefined

Hi Dan from Unknown

Hi Dan from Denver

Hi Unknown from Unknown

Hi Unknown from Denver

Error

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

Unknown, Unknown, Joburg

Unknown, Unknown, Unknown

Unknown, `undefined`, Joburg

N/A, `undefined`, Joburg

N/A, Unknown, Joburg

N/A, N/A, Joburg

Unknown, N/A, Joburg

Error

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.

N/A, N/A

N/A, undefined

Unknown, N/A

Unknown, Unknown

Unknown, undefined

null, N/A

null, Unknown

null, undefined

Error

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.

N/A

undefined

Unknown

'null'

TypeScript Error

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.

undefined

null

N/A

Unknown

Denver

SyntaxError

Error: Invalid type

Error: Invalid Arguments

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.

Error: Property 'first' is missing

Error: Property 'last' is missing

Error: Properties 'birth' and 'age' are missing

Error: Property 'place' is missing

Error: 'string' has no properties in {...}

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}`); // ✅

Hi Dan Levy from N/A

Hi Dan Levy from Cape Town

Hi N/A N/A from N/A

Hi N/A N/A from Cape Town

Error

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!

Read the original on danlevy.net

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.