Table of Contents
Optional Chaining is one of the newest features in JavaScript. This feature may seem insignificant. However, it can save you a lot of time, code and also a lot of headaches. In this tutorial, you will learn what this feature is about, how it works and how to use it to write better JavaScript code.
Defying the problem
Have you ever worked with objects? Then, you know how easy it is to run into following problem. Let’s say you have an object. This object has some properties and maybe also some methods. Next, let’s say that you want to work with some of these properties or methods.
Getting this done is very simple. You can access any property using either dot or square bracket notation. The same applies to methods. What happens is if you try to access some property, or method, that doesn’t exist in that object? When you try to access property that doesn’t exist you will get undefined
.
// Create an object
let myObj = {
name: 'Joe Saladino',
email: 'joe@saladino.io'
}
// Try to access non-existing property "location"
console.log(myObj.location)
// Output:
// undefined
What if you try to access some property that is nested deeper? Imagine you have some object. This object contains some properties. The value of one of these properties is supposed to be also an object. This object should contain some additional properties. What if this nested object doesn’t exist?
What happens if you try to access some property in that non-existing nested object? You will not get undefined
. What will you get instead is a TypeError
. JavaScript will complain that it can’t read property of an object that is not defined.
// Create an object
let myObj = {
name: 'Joe Saladino',
email: 'joe@saladino.io'
}
// Try to access non-existing property "location"
console.log(myObj.location)
// Output:
// undefined
// Try to access non-existing property "city"
// in non-existing object "location"
console.log(myObj.location.city)
// Output:
// TypeError: Cannot read property 'city' of undefined
Solving the problem the “old” way
Solving this problem this “old” way would mean using the logical AND (&&
) operator. Let’s try to solve the problem with the non-existing property city
in a non-existing object location
using the &&
operator.
// Create an object
let myObj = {
name: 'Joe Saladino',
email: 'joe@saladino.io'
}
// Try to access non-existing property "city"
// in non-existing object "location"
// using the && operator
console.log(myObj && myObj.location && myObj.location.city)
// Output:
// undefined
A quick introduction to optional chaining
As you can see, solving the problem with the non-existing property in a non-existing object with &&
operator is easy. The downside of this solution is that it requires more code. How much code will you have to write depends on how deep you need to get.
// Create an object
let myObj = {
name: 'Joe Saladino',
email: 'joe@saladino.io'
}
// Going down the rabbit hole
console.log(myObj && myObj.location && myObj.location.city && myObj.location.city.address && myObj.location.city.address.houseNumber)
// Output:
// undefined
How optional chaining works
Thanks to optional chaining, all that code is no longer necessary. The way optional chaining works is simple. Let’s say you use it to access some property. If any part before the property you want to access is either undefined
or null
it will stop the evaluation and return undefined
.
Let me put it this way. With optional chaining, JavaScript will always first test any property, that precedes the one you want to access, if it exists. If it does exist JavaScript will move to the next property until it reaches the one you want to access. If it doesn’t exist it will return undefined
.
The syntax
The syntax of optional chaining is very simple. All you have to do is use ?.
operator. The way to use this operator is to put it between the object and the dot that precedes the property that may not exist. For example, myObj.myProp1?.myProp2
will ensure the myProp1
exists before trying to access myProp2
.
Solving the problem with optional chaining
Let’s demonstrate how optional chaining works by using it to solve the problem non-existing property city
in a non-existing object location
. In this example, you were trying to access non-existing property city
. This property was supposed to exist in non-existing property/object location
.
What you have to do is to ensure the location
property/object actually exists, before you try to access any property inside it. To do this, you will put the ?.
operator right after the location
property and before the .city
. So, myObj.location?.city
. This will correctly return undefined
, not TypeError
.
// Create an object
let myObj = {
name: 'Joe Saladino',
email: 'joe@saladino.io'
}
// Try to access non-existing property "city"
// in non-existing object "location"
// using optional chaining
console.log(myObj.location?.city)
// Output:
// undefined
Going down the rabbit hole
When you need to go deeper, the process is the same. All you have to do is to put the ?.
operator right after the object property that may not exist and right before the dot and property you want to access. You can repeat this for any number of properties you want or need.
// Create an object
let myObj = {
name: 'Joe Saladino',
email: 'joe@saladino.io'
}
// Try to access "houseNumber" property
// that is supposed to be in "address"
// that is supposed to be in "city"
// that is supposed to be in "location"
console.log(myObj.location?.city?.address?.houseNumber)
// Output:
// undefined
Optional chaining and methods
Just like with properties, you can use the optional chaining operator also with methods. The process is the same as with properties. You put the ?.
operator right after the object property that may not exist, and right before the dot and method you want to call.
If the property doesn’t exist, you will get undefined
. If it does exist JavaScript will try to access the method. If the method exists, it will be invoked. Otherwise, you will again get undefined
.
// Create an object
let myObj = {
name: 'Jack Trout',
email: 'jack@trou.ai'
}
// Try to call "sayHi()" method directly
console.log(myObj.methods.sayHi())
// Output:
// TypeError: Cannot read property 'sayHi' of undefined
// With the "?" operator:
// Try to call "sayHi()" method
// that is supposed to exist on "methods" property
// that is supposed to exist on "myObj" object
console.log(myObj.methods?.sayHi())
// Output:
// undefined
There is another thing you can do. You can use the optional chaining operator to check if the method itself exist before you call it. In this case, you have to put the ?.
operator before the parentheses used to invoke the method. Then, you have to add another dot and only then the parentheses.
// Create an object
let myObj = {
name: 'Victoria Wales',
email: 'joe@saladino.io'
}
// Try to call "sayHi()" method directly
console.log(myObj.sayHi())
// Output:
// TypeError: myObj.sayHi is not a function
// With the "?" operator:
// Check if "sayHi()" method exists before you call it
// that is supposed to exist on "methods" property
// that is supposed to exist on "myObj" object
console.log(myObj.sayHi?.())
// Output:
// undefined
// Or if the method is nested
console.log(myObj.methods?.sayHi?.())
// Output:
// undefined
Using the ?.
operator for calling a method if the thing you want to call is not a method will not work. For example, let’s say that sayHi
is not a method, but a property. If you try to call it, with ?.
operator, JavaScript will still throw TypeError
saying that sayHi
is not a function.
So, make sure that the method you want to call is really a method. If it is something else, it will still lead to JavaScript throwing an error.
// Create an object
let myObj = {
sayHi: 'Hi'
}
// Try to call property "sayHi"
console.log(myObj.sayHi?.())
// Output:
// TypeError: myObj.sayHi is not a function
Optional chaining and bracket notation
You can also use the ?.
operator when you want to access a property using bracket notation. In this case, the ?.
operator goes right after the object name. Next comes a dot and after that come the square brackets and property name.
// Declare new variable and set it to null
const myObj = null
// Try to access "name" property on null value directly
console.log(myObj['name'])
// Output:
// TypeError: Cannot read property 'name' of null
// With the "?" operator:
console.log(myObj?.['name'])
// Output:
// undefined
This approach also works if you want to access item inside an array. This assumes that the array is supposed to exist inside some object. Normally, accessing item inside an array that doesn’t exist inside an object would lead to another TypeError
. The ?.
operator can help you avoid this.
// Declare empty object
let myObj = {}
// Try to access item inside an array
// that is supposed to exist inside "myObj"
console.log(myObj.languages[3])
// Output:
// TypeError: Cannot read property '3' of undefined
// With the "?" operator:
console.log(myObj?.languages[3])
// Output:
// undefined
Undefined variables
Optional chaining works only with existing variables. If you try to use it on some undefined (non-existing) variable JavaScript will always throw a ReferenceError
. So, make sure the variable you want to work with exists because the ?.
operator can’t help you here.
// Try to access property in undefined variable
console.log(myUndefVar.myProp)
// Output:
// ReferenceError: myUndefVar is not defined
// With "?" operator:
console.log(myUndefVar?.myProp)
// Output:
// ReferenceError: myUndefVar is not defined
Conclusion: Optional chaining in JavaScript and how it works
Optional chaining is one of those features that are easy to understand, simple to use and yet be very handy. It can save you a lot of time and headaches while writing less code. I hope this tutorial helped you understand what optional chaining is, how it works and how to use it.
If you liked this article, please subscribe so you don't miss any future post.
If you'd like to support me and this blog, you can become a patron, or you can buy me a coffee 🙂