Introduced in ES2020, optional chaining allows access to properties in an object in JavaScript in a safe manner. Using the optional chaining operator (?), one can check within nested objects the value of a property without having to check whether every single reference that is in the chain is either undefined or null. If any of the references are found to be either undefined or null, the operator will perform a ‘short circuit’ and undefined will be returned instead of throwing an error. The rest of the expression to the right of the optional chain operator is not evaluated.

Examples:

Objects

const userObj = {
    firstName: 'Tom',
    lastName: 'Jones',
    preferences: {
        theme: 'dark',
        rememberLogin: true,
    }
};
const userTheme = userObj.preferences.theme?; 

console.log(userTheme); //dark

In the line above, we are trying to retrieve the user’s preferred theme. Using the optional chaining operator (?), we check whether the theme property exists in the nested object preferences. Once that exists, it will retrieve the value of the theme property.

Only use optional chaining when checking the existence of something that is optional. In the example above, we only used the optional chaining operator on the theme property, since its existence is optional. The existence of both a user object (userObj) and a preferences object nested within the user object are required.

console.log(user?.color) //ReferenceError: user is not defined

As you can see above, if we simply try to retrieve the color property on an undeclared object called user with the optional chaining operator, we will get a ReferenceError. In this case, whether the optional chaining operator was used or not, the same message will be returned because the user object is undeclared.

Functions

We can also use optional chaining with functions. If the function cannot be found, undefined is returned rather than an exception being thrown.

class User {
  greetUser (name) {
    return `Hello ${name}`;
  }; 
}

let user = new User();

console.log(user.greeUser.('Tom')) //TypeError: user.greeUser is not a function

console.log(user.greeUser?.('Tom')) //undefined

console.log(user.greetUser?.('Tom')) //Hello Tom

Optional chaining with nullish coalescing

We can combine optional chaining with nullish coalescing like so:

const employee = {
    firstName: 'John',
    lastName: 'Smith',
    profile: {
        position: 'Engineering Manager'
    }
}

const employeeDepartment = employee?.profile.department ?? 'Not Available';

//OR another way

const employeeDepartment = employee?.['profile' + 'department'] ?? 'Not Available';

console.log(employeeDepartment); //Not Available

Using optional chaining with arrays

We can also use optional chaining with arrays:

const array = [1, 5, 7, 3, 2, 9, 8, 0, 6];

console.log(array?.[3]); //3

console.log(array?.[20]); //undefined

Optional chaining and writing to an object property

You cannot use optional chaining in order to write to an object property. It can only be used for reading and deleting.

const employee = {
    firstName: 'John',
    lastName: 'Smith',
    profile: {
        position: 'Engineering Manager'
    }
}

let assignDepartment = employee?.profile.department = 'IT'; //SyntaxError: invalid assignment left-hand side

//Delete the position
delete employee?.profile.position; //True

console.log(employee); // Object {
                            firstName: 'John',
                            lastName: 'Smith',
                            profile: Object {}
                          }