MatheusMorais.dev
This post is also available in Português
Drops #1

Javascript Optional Chaining Operator (?.)

You're reading a Drop: A Quick Tip, or something I've learned recently that I'd like to share.

The Optional Chaining Operator (?.) is a JavaScript operator that lets you access properties of a object without doing explicit checks (using an if statement, or a ternary operator (condition ? expr1 : expr2), for example) on the existence of the object itself. And that's amazing, mainly because it's quite common to have several objects chained together, where some of them may be null or undefined in some application state.

The problem

Let's see a very simple example:

Imagine that, in some state of my application, we'll have an object user that will have another chained object called contact that will have some contact properties:

const user = {
contact: {
phone: '(000) 000-0000'
}
};

But let's assume this contact data is not available in an initial state, so my user object will be empty:

const user = {};

With just what we have here, we can't know what state our application is in. Therefore, we will not be able to know if the contact object exists in the user object. So... What happens if we try to access the properties of the contact object in different states?

Well, if the contact object is on user as expected, then it'll work correctly:

const user = {
contact: {
phone: '(000) 000-0000'
}
};
console.log('Phone:', user.contact.phone);
// ✅ Works!

But if the contact object is not yet in the user object, then:

const user = {};
console.log('Phone:', user.contact.phone);
// ❌ Uncaught TypeError:
// Cannot read properties of undefined (reading 'phone')

The contact object hasn't been defined in the user object yet, so we can't access its properties.

Using the optional chain operator

As already said, there are several ways to solve this, but the optional chaining operator keeps everything very clean and simple:

console.log('Phone:', user.contact?.phone);
// here ^
  • If user.contact exists, then we will be able to access the phone property and display it.
  • If user.contact is null or undefined, then the optional chaining operator will return undefined and will not even attempt to access the phone property.

Possibilities

In addition to chained objects, we can use the operator in functions, and even in arrays:

  • Functions:

    const photo = user.viewPhoto?.();

    It will only run the displayPhoto function if it isn't null or undefined. Otherwise, photo will be undefined.

  • Arrays:

    const user = {
    contacts: [{ phone: '(000) 000-0000' }, { phone: '(111) 111-1111' }]
    };
    const firstPhone = user.contacts?.[0]?.phone;

    Here we can see two possibilities:

    • In contacts?., we check if the contacts array already exists in the user object, before proceeding to access the rest of the chain (which, in this case, is trying to access the first element of the array).
    • If contacts?. returned the array (instead of undefined), as expected, then we go ahead and check if the first element exists: [0]?..
    • If we have gone through both optional chaining operators, then we are able to access the phone property. If not, we'll have undefined as the value of the constant firstPhone.

TL;DR:

(Portuguese only) You can see a summary of this post on Reels at my tiktok account or my instagram account: