The beauty of javascript is, that it allows you to dynamically add behaviors and states in an object at runtime. So an object can have more properties than what was mentioned during the declaration.
It even allows you to dynamically update an object's prototype chain. So all other objects of the same type get access to these updated behaviors or states.

The beauty of javascript is, that it allows you to dynamically add behaviors and states in an object at runtime. So an object can have more properties than what was mentioned during the declaration.
It even allows you to dynamically update an object's prototype chain. So all other objects of the same type get access to these updated behaviors or states.
The Problem
To support this dynamic addition feature, Javascript allows us to write code that accesses any random property on an object with a (.
) operator. It does not verify if that property should be accessed or not.
For example, If you have a Vehicle, which can be any valid vehicle, like a car, airplane, bike, ship, etc. then in a badly designed codebase, you can encounter the below code.
let flightDuration = vehicle.flightDetails.duration
Let's assume the vehicle object we are dealing with, is a Car, In 2022 it's fair to assume that a car can not fly. For a car the expression vehicle.flightDetails
will return a nullish reference (null
or undefined
). Accessing any property on the flightDetails object for a car-type-vehicle will throw Cannot read properties of undefined
TypeError.
To support this dynamic addition feature, Javascript allows us to write code that accesses any random property on an object with a (.
) operator. It does not verify if that property should be accessed or not.
For example, If you have a Vehicle, which can be any valid vehicle, like a car, airplane, bike, ship, etc. then in a badly designed codebase, you can encounter the below code.
let flightDuration = vehicle.flightDetails.duration
Let's assume the vehicle object we are dealing with, is a Car, In 2022 it's fair to assume that a car can not fly. For a car the expression vehicle.flightDetails
will return a nullish reference (null
or undefined
). Accessing any property on the flightDetails object for a car-type-vehicle will throw Cannot read properties of undefined
TypeError.
The old solution
To avoid the above runtime error, for ages Javascript developers have been writing code that short-circuits the nullish references. It’s done by checking the nullish references on outer properties, before accessing any nested property. So below is a production-ready version of the above code
let flightTime = vehicle.flightDetails &&
vehicle.flightDetails.duration ;
However, let's assume that a flight duration is an object containing, hours, minutes, and seconds.
duration: {
hours: “4”,
minutes: “20”,
seconds” “40"
}
NOTE: Time durations should be represented in milliseconds or nanoseconds but as I’m writing this post while sitting on a delayed flight. For combining my experience with nested nullish references, let's represent it this way.
So if we want to find the flying hours taken by a car ,we will write following expression
let flightHours = vehicle.flightDetails &&
vehicle.flightDetails.duration &&
vehicle.flightDetails.duration.hours;
As you can see above, the deeper we go in an object graph, the more code we need to write to avoid the TypeError. This is where Optional Chaining comes for rescue
To avoid the above runtime error, for ages Javascript developers have been writing code that short-circuits the nullish references. It’s done by checking the nullish references on outer properties, before accessing any nested property. So below is a production-ready version of the above code
let flightTime = vehicle.flightDetails &&
vehicle.flightDetails.duration ;
However, let's assume that a flight duration is an object containing, hours, minutes, and seconds.
duration: {
hours: “4”,
minutes: “20”,
seconds” “40"
}
NOTE: Time durations should be represented in milliseconds or nanoseconds but as I’m writing this post while sitting on a delayed flight. For combining my experience with nested nullish references, let's represent it this way.
So if we want to find the flying hours taken by a car ,we will write following expression
let flightHours = vehicle.flightDetails &&
vehicle.flightDetails.duration &&
vehicle.flightDetails.duration.hours;
As you can see above, the deeper we go in an object graph, the more code we need to write to avoid the TypeError. This is where Optional Chaining comes for rescue
Optional Chaining
In Optional Chaining, we can use the (?.
) operator to access the properties located deep inside an object graph. whenever the operator ?.
encounters a nullish reference at its left, it short-circuits the expression & returns undefined
So we can write the above verbose production-ready code as below
let flightHours = vehicle.flightDetails?.duration?.hours;
In Optional Chaining, we can use the (?.
) operator to access the properties located deep inside an object graph. whenever the operator ?.
encounters a nullish reference at its left, it short-circuits the expression & returns undefined
So we can write the above verbose production-ready code as below
let flightHours = vehicle.flightDetails?.duration?.hours;
Optional Chaining with functions
You can use optional chaining with functions too. if the function at the left of ?.
doesn’t exist, the expression is short-circuited resulting in undefined
Below code will set flightHours variable as undefined
let flightHours = vehicle.flightDetails?.duration?.getMiliseconds()?.convertToHours();
Without optional chaining, the code will throw the TypeError, getMilisecondsis not a function.
let flightHours = vehicle.flightDetails.duration.getMiliseconds().convertToHours();
This was a quick introduction to Optional chaining, Optional Chaining helps us write clean and concise production-ready code. We will discuss some more Javascript tricks in the next post. stay tuned.
You can use optional chaining with functions too. if the function at the left of ?.
doesn’t exist, the expression is short-circuited resulting in undefined
Below code will set flightHours variable as undefined
let flightHours = vehicle.flightDetails?.duration?.getMiliseconds()?.convertToHours();
Without optional chaining, the code will throw the TypeError, getMilisecondsis not a function.
let flightHours = vehicle.flightDetails.duration.getMiliseconds().convertToHours();
This was a quick introduction to Optional chaining, Optional Chaining helps us write clean and concise production-ready code. We will discuss some more Javascript tricks in the next post. stay tuned.
Comments