Skip to main content

[[TOC]]

Optional Chaining


MDN Docs Often you see code that looks like this to make sure that there are properties that are defined - this is more error handling because if one of these properties are not defined, it will throw an error.

Note: You cannot use optional chaining on an object that may not exist. This means when you access user.details?., you need to make sure that there is a variable (of any type) called user. Otherwise, you get an error.

// assuming object `user`

let name = undefined;
if (user.details && user.details.name && user.details.name.firstName) {
name = user.details.name.firstName;
}

With Optional chaining, you can take all of that code above and do this:

// assuming object `user`

const name = user.details?.name?.firstName;

Let's look at this deeper:

const user = {
details: {
name: {
firstName: "Solo"
}
},
data: null
}

user.details?.name?.firstName; // "Solo"
user.data?.id; // undefined
user.children?.names; // undefined
user.details?.parent?.firstName; // undefined

Try it:

const getFullName = user => {
return user.info?.name //<-- this returns OK if name is not present

}

// Sample usage - do not modify
console.log(getFullName({info: {name: "Sam"}})); // "Sam"
console.log(getFullName({info: null})); // undefined
console.log(getFullName({})); // undefined

Rewrite this so that you don't have an if statement:

const getPaymentValue = user => {
if (user.payment && user.payment.details && user.payment.details.value) {
return user.payment.details.value;
}
return undefined;
}

// Sample usage - do not modify
console.log(getPaymentValue({id: 1, name: "Alex"})); // undefined
console.log(getPaymentValue({id: 2, name: "Sam", payment: {details: {value: 59}}})); // 59

Solution

const getPaymentValue = user => {
return user?.payment?.details?.value
}

// Sample usage - do not modify
console.log(getPaymentValue({id: 1, name: "Alex"})); // undefined
console.log(getPaymentValue({id: 2, name: "Sam", payment: {details: {value: 59}}})); // 59


Optional Chaining - a bit more advanced


// This example here
const data = {
temperatures: [-3, 14, 4]
}

let firstValue = undefined;
if (data.temperatures) {
firstValue = data.temperatures[0];
}
// this can be refactored into
const firstValue = data.temperatures?.[0];

Chaining with functions

// We can take this:
const person = {
age: 39,
name: "Solo"
};

let upperCasedName = person.name; // might be undefined
if (person.name) {
upperCasedName = person.name.toUpperCase();
}

// and refactor into this:
const upperCasedName = person.name?.toUpperCase();
// This will only call the .toUpperCase() method if person.name does not evaluate to null or undefined.
// Optional chaining helps you avoid these errors by returning undefined.

Another quick example:


const person = {
age: 39,
};

const upperCasedName = person.name?.toUpperCase(); // undefined

Don't overuse. When ?. is found in the code, it means that there's a moderate chance that the value returns undefined. In turn, this means that we should be handling the case when it returns undefined.

Don't use for assignment


const settings = {};

// ❌ Syntax Error
settings?.theme = "dark";

// instead, use:
const settings = {};

settings?.theme && (settings.theme = "dark");
console.log(settings); // {}

Optional Chaining Recap


  • Optional chaining can be used for arrays. The syntax is ?.[index]
  • Optional chaining can be used for functions. The syntax is functionName?.()
  • Optional chaining cannot be used for assignment. It's only used for reading.

#Try more

//Redo this without using if conditions:
const getFirstGrade = data => {
if (data.results && data.results.grades) {
return data.results.grades[0];
}
return undefined;
}

// Sample usage - do not modify
console.log(getFirstGrade({results: {grades:[ 18, 14, 19]}})); // 18
console.log(getFirstGrade({results: {}})); // undefined
console.log(getFirstGrade({})); // undefined

// solution
const getFirstGrade = data => {
return data.results?.grades?.[0];
}

To lowercase added


Complete the function getFullName such that it returns the full name in lower case from the user object when it exists. Otherwise, it should return undefined.

const getFullName = user => {

}

// Solution:
const getFullName = user => {
return user.info?.name?.toLowerCase();

}

Recap:


  • Optional chaining allows you to access a property deep within an object without risking an error if one of the properties is null or undefined.
  • In case one of the properties is null or undefined, then the ?. will short-circuit to undefined.
  • You cannot use optional chaining on an object that may not exist. The object has to exist. Optional chaining is only used to access a property that may or may not exist.
  • Optional chaining can be used for arrays. The syntax is ?.[index]
  • Optional chaining can be used for functions. The syntax is functionName?.()
  • Optional chaining cannot be used for assignment. It's only used for reading.