Safely Access Nested Object Properties with Optional Chaining
JavaScript's optional chaining operator was introduced as part of ES2020.
This operator allows developers to access deeply nested properties within objects while automatically handling any edge cases where a property may be undefined
or null
.
Consider an object called movie
:
const movie = { title: "Oppenheimer", director: { name: "Christopher Nolan", birthYear: 1970, },};
It has a title
, Oppenheimer
, a director
property, which is a nested object, and a director
has a name
and a birthYear
.
In order to access birthYear
, we could do movie.director.birthYear
if we knew it was there:
const birthYear = movie.director.birthYear;// Output: 1970
However, let's suppose that we're working with some data where we're not exactly sure of the shape. In the real world, our data often is missing or incomplete or structured incorrectly.
For example, let's say that for some reason Director
has a capital 'D', or it's just missing entirely.
Here we have a different movie, this time it's Barbie
, and we assume that all movies have information about the director.
const movie = { title: "Barbie",};const birthYear = movie.director.birthYear;// Output: Uncaught TypeError: Cannot read properties of undefined (reading 'birthYear')
In this case, when we try to access the birthYear
property, we get an error.
If we were to manually check for the existence of the birthYear
property, we could do something like this:
const birthYear = movie && movie.director && movie.director.birthYear;
However, this is pretty long and clunky.
The Optional Chaining Operator ?.
To handle this more smoothly, we can use the optional chaining operator ?.
.
Extracting the birthYear
only if the director
exists would look like this:
const birthYear = movie?.director?.birthYear;
This code checks if movie
, movie.director
, and movie.director.birthYear
all exist.
The optional chaining operator (?.
) checks if the preceding value is null
or undefined
. If it is, the entire expression short-circuits, returning undefined
.
For example, if the movie
did not have a director
at all, the expression would return undefined
.
Optional Chaining with Functions
The optional chaining operator can also be used when calling a function or method that may or may not exist.
Like before, you can use it by adding a question mark after the property name, followed by parentheses:
const movie = { director: { birthYear: 1970, }, getMovieData: () => "aksjdlkasjdlkas"};movie.getMovieData?.();
In this case, the movieData
will give us the result of calling the getMovieData
function if it exists.
If movie
or movie.getMovieData
are null
or undefined
, the whole expression short-circuits, and movieData
will be undefined
.
Optional Chaining with Arrays
Say the movie
has a property called actors
with an array of actors:
const movie = { // properties as before actors: ['RDJ', 'Cillian Murphy']}
If we wanted to get the second element out of actors
, we could do something like this:
movie.actors[1]
However, if we're not sure whether actors
exists or not, we can use optional chaining to avoid errors by adding the ?
after actors
:
movie.actors[1] // Output: Cillian Murphymovie.actors?.[1] // Output: Cillian Murphymovie?.actors?.[1] // Output: Cillian Murphy
Now, if we misspell actors
, the optional chaining will prevent an error:
console.log(movie.actrs?[1]); // Output: undefined
Wrap Up
Optional chaining in JavaScript is a useful syntactic sugar that allows us to tentatively try and access properties off of objects that may or may not exist, making our code more safe and easier to read than handling edge cases manually.
Transcript
00:00 JavaScript's optional chaining operator, which was introduced as part of ES 2020, allows developers to access deeply nested properties within objects while automatically handling any edge cases where a property may be undefined or null. So I have a very simple object
00:16 here called movie. It has a title, Oppenheimer, a director property, which is a nested object, and a director has a name and a birth year. So of course, in order to access birth year, if I knew for sure that it was in there, nested under director, I could do movie.director.birth
00:33 year. But let's suppose that I'm working with some data where I'm not exactly sure of the shape. It's kind of silly when I have to find it right here for you, but in the real world, our data often is missing or incomplete or structured incorrectly. If I run this line,
00:48 movie.director.birth year, we definitely get the correct answer, 1970. But let's say that for some reason, director has a capital D, or it's just missing entirely, right? We have a different movie. This time it's Barbie. And we assume that all movies have information about the director.
01:07 But in this case, if I copy this and run it, we get an error, right? We get a type error. Cannot read properties of undefined because director is undefined. We can't do something like undefined.birth year. That generates a type error. So in the past, without the optional
01:25 chaining operator, the way we would do this safely, if we wanted to extract birth year, only if director exists, would look something like this. This is one option, at least. Movie and and movie.director and and movie.director.birth year.
01:44 And then I could save this to a variable. I'll call it year. And if we look at year, it's going to be undefined, but there's no error involved. This is totally valid. We're just using some basic Boolean logic. So if movie.director doesn't exist, like it doesn't here, right? This
02:00 whole thing is now going to be false. And we'll end up with undefined as the return value that is stored in year. So this is kind of the old way of doing it. Now, to be clear, this right here does not protect against, let's do something that doesn't exist, like movie with a B. We'll still
02:16 get a reference error here, because JavaScript doesn't know what this is referencing. So this is a pretty long and clunky way to safely access birth year. Enter the newish optional chaining operator, which is written with a question mark, followed by a period. So I could rewrite this line
02:33 that you see here. I'll comment it out to look like this. Movie question mark, followed by a question mark, dot, director, question mark, and then dot, birth year. Let me expand this so you
02:51 can see it on one line. The question mark dot is going to check to see if the preceding value is null or undefined. And if it is, the entire expression, short circuits, returning undefined. So in this case, movie has a title, and movie is defined, so this is fine. But then we get to dot
03:09 director. Well, there is no director property, right? That evaluates to undefined, which will short circuit the whole thing, and year will be undefined. As you can see over here, it's set to undefined. Just like what we had here, but this is much shorter and much more intuitive. The question
03:24 mark makes a lot of sense as a choice, right? We're questioning, is there director here? If so, let's get birth year. And again, if at any point, movie or movie dot director are null or undefined, the whole thing short circuits, and we end up with undefined. But if I restore this to have a
03:42 director, I don't know Greta Gerwig's birth year, so I'm going to go back to Oppenheimer. And his birth year, Christopher Nolan, make sure I spell it correctly, was 1970. If I rerun this,
03:58 now year is in fact set to 1970, because this evaluated not to be null or undefined. This evaluated not to be null or undefined, and then we access birth year, and that's going to give us 1970. We can also use the optional chaining operator when calling a
04:13 function or method that may or may not exist. So I'll add a simple function in here called, I don't know, get movie data, and it will just return some nonsense. Okay, so if I want to call
04:28 it, I could do movie dot get movie data, and just assume that it's always there. But of course, if it's not there, and then try and execute that as a function, I get a type error, right? That doesn't exist, undefined, it's not a function. So what I can do is put a question mark, and then a
04:44 dot, and then follow it with the parentheses. So movie dot get movie data, does that exist? Is that thing not undefined and not null? If so, we'll try and execute it. If it is undefined or
04:58 null, then don't execute it. So in this case, it works. I mean, why don't I console dot log, so you can just see that it does work. And we get our console dot log printing out. But if it doesn't exist, or it's misspelled, we don't get an error. And the same is true of working with
05:18 arrays. So if I have an array in here called, I don't know, actors. And, oh gosh, who is in this movie? Robert Downey Jr. is in Oppenheimer. I'll just do rdj. What's his name? Killian Murphy. Let's just leave it at that. I don't know if I spelled his name correctly. Okay, if I want to
05:38 access the second element out of actors, if I just assume that it exists, it would be something like this, right? Movie dot actors, and we get Killian Murphy. But if I'm not sure, movie dot actors exists, same exact syntax, question mark dot. And then if it does exist, it's not null, and it's not
05:57 undefined, we'll access the first element out of the array. But if I misspell it, or it's not on my data, I'll instead end up with undefined instead of an error, which is what I get here. So optional chaining is super useful. It's a nice little piece of syntactical enhancement to JavaScript,
06:16 or sugar. We use a question mark followed by a dot to tentatively try and access a property off of an object that may or may not exist. And it can simplify our code significantly. And remember that if at any point the value before question mark dot evaluates to null or undefined,
06:34 the whole expression will short circuit and return undefined.