Originally published on Full Stack Heroes blog.
Building real-world applications means working with data, often stored as objects.
In real world, data is complex and nested multiple levels deep. An object has a property with a value of another object, and that object might store yet another one. To make things worse, data is often unpredictable – sometimes property values are null, undefined, or simply not there.
In this article, we will go over optional chaining operator, a new JavaScript feature that allows you to check the existence of a property before accessing it.
Common Solution
For a long time, developers used the behavior of && logical operator to check the existence of property before accessing it.
Let’s imagine we have a person object, with properties nested multiple levels deep.
const person = {
name: "John Doe",
age: 55,
occupation: "farmer",
address: {
state: "North Carolina",
city: "Lillington",
street: "1937 Powell Farm Rd",
zip: 27546,
shipping_address: {
street: "1937 Powell Farm Rd",
zip: 27546
}
}
};
Commonly used solution to check the existence of a property (let’s say ‘zip’) is to use the logical AND operator:
const zipCode = person.address && person.address.zip
console.log(shippingZip)
// expected output: 27546
The logical AND operator checks if the value of person.address property is null or undefined. If it is, it performs an early exit and returns undefined.
If the value of person.address is a valid object, then the logical AND operator returns the nested value of person.address.zip property.
This syntax is fine if you want to access properties one or two levels deep.
The expression gets way too complicated when you want to access property multiple levels deep. For example, the zip code of the shipping address:
const shippingZip = person.address ?? person.address.shipping_address ?? person.address.shipping_address.zip
console.log(shippingZip)
// expected output: 27546
Writing such repetitive, long, unreadable expressions is inefficient. That’s why JavaScript team introduced optional chaining operator.
JavaScript optional chaining operator Example
Optional chaining operator provides simpler, more readable syntax to check the existence of properties before accessing them. It saves you the trouble of manually checking each reference in the chain.
The rules are simple – if there’s a chance that property doesn’t exist, simply use the ?.
instead of normal dot notation on its right.
const zipCode = person.address?.zip
// if the person object is within scope, zipCode will be set equal to 27546
Syntax is much cleaner. This is more noticeable when you’re chaining multiple property accesses.
const shippingZip = person.address?.shipping_address?.zip
// if the person object is within scope, zipCode will be set equal to 27546
For me, the question mark signifies the uncertainty of the property’s existence. So the syntax is very readable and descriptive of its use case.
It works exactly like the solution with logical AND operator.
If any property in the chain is null, undefined, or just doesn’t exist, the expression automatically short-circuits, returning undefined, saving you from a possible error.
Calling a method
Optional chaining operator can prevent an error when trying to call a method that doesn’t exist, or is not available. (due to version of the API, or incompatibility of user’s device).
When trying to call methods that don’t exist, optional chaining operator will short circuit to undefined, like it does for properties.
If you try to call property as a method, you will still get an error:
TypeError obj.method is not a function
Check the existence of object itself
Optional chaining operator is not restricted to checking properties of objects. You can use it to check the existence of object itself.
const zipCode = person?.address.zip
// right-side expression will return undefined if the person object can not be found
Is just a shorter, more readable way to write:
const zipCode = (person === null || person === undefined) ? undefined : person.address.zip
A possible use case for this feature is to avoid errors when you’re trying to access external API data before it is loaded.
Dynamic property access
Bracket notation allows you to dynamically determine the property you’re trying to access.
Here’s how to use JavaScript optional chaining operator with this syntax:
const zipCode = person?.[‘add’+’ress’].zip
// will be evaluated as person?.address.zip
Stacking property accesses
If the optional chaining operator finds property that does not exist, it will short-circuit and return undefined. If there is a bracket notation that contains an expression, it will not be executed.
const zipCode = person.address?.["zi" + "p"]
// if the address property does not exist, will return undefined, before evaluating expression between brackets
If the address property does not exist, the expression between opening and closing brackets will not execute.
JavaScript optional chaining operator with array index
Bracket notation is often used to look up items at specific index in the array.
Optional chaining operator can be useful if you’re unsure whether an array has item at that index.
For example:
const arr = [1,2,3,4]
const item = arr?.[5]
console.log(item)
// will return undefined
Array does not have 6 items, so there is no item on index 5. Thanks to optional chaining operator, JavaScript will not throw an error.
Nullish coalescing operator to set the default value
Optional chaining operator gives you the ability to access object properties when possible, but avoid errors if properties do not exist or are inaccessible.
You can go one step further and instead of short-circuiting to undefined
, set a default value to be returned when property value is inaccessible.
Syntax is simple: after you try to access a property, follow it with double question marks (??
) to set the backup value.
const zipCode = person.address?.zip ?? "zip code not found"
console.log(zipCode)
// if property access is successful, value of the property will be output to the console
// if property access is not possible, console will return the string "zip code not found"
What NOT to do with JavaScript optional chaining operator
This syntax can not be used to assign value to a property of an object.
Using optional chaining operator on the left side of equation is wrong:
person.address?.zip = 19805 // this is wrong
Another common mistake is overusing optional chaining operator, which makes your code hard to debug, because it’s difficult to track down which part of the code returns undefined. This practice is called error silencing.
Practical use cases for JS optional chaining operator
Using this syntax can help you improve readability and reduce the length of your code.
Because of its simplicity, JavaScript developers often use optional chaining operator too much.
Let’s go over good use cases for this syntax.
Examples of Good Practical use cases
As a general rule of thumb, optional chaining operator is best used with external data, which is often unpredictable.
For instance, when loading data from API, response can be delayed for one reason or another. Trying to access external data before it’s loaded is source of errors, which can be avoided by using optional chaining operator.
Using JavaScript optional chaining operator when filtering data
External data is often formatted as an array of objects. It’s common to use methods like find()
and filter()
to find objects that meet a certain criteria.
For example, for an ecommerce store, each object can represent one product. An array can contain thousands of such objects. Naturally, list of products is constantly updated – a store might add a new product and remove some of the old ones.
You might need to use these methods to filter the list of all products (objects) to display the ones that cost more than $10, for example.
Sometimes calling filter()
and find()
methods returns an empty array, as there are no qualifying objects. Alternatively, the property you are trying to access may be optional, and absent from certain objects.
You can use JavaScript optional chaining operator to access properties of filtered objects. If the filter()
or find()
method returns no objects, expression will simply return undefined.
let products = [{name: "coca cola", cost: 4, inStock: true},..., {name: "fanta", cost: 3, inStock: true}]
let filteredProducts = products.filter(product => product.cost > 5)
console.log(filteredProducts?.[0])
// if filteredProducts is empty, the expression above will return undefined
Use optional chaining operator to call a method
You could also use it when trying to call method on external interface. The interface or specific method might not be available, in which case you can avoid errors by using optional chaining operator.
JavaScript optional chaining operator is useful to access optional properties that might not have a value.
Conclusion
In this article, we discussed JavaScript optional chaining operator. This new feature makes it very easy to check nested properties without writing many lines of confusing code.