[[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.