This post is also available in different formats so you can read on the go or share it around!
null
and undefined
are used in JavaScript to represent the absence of a value, and they can take some care when dealing with them. There are a couple of ways we can approach the problem, so let’s have a look at the oldest and most used. Then we’ll have a look at a newer feature that makes object access much easier.
Explicit checks
The tried and true way to check for null/undefined is by comparison.
const checkPantry = (juice) => {
if (juice) {
console.log(`we have ${juice} juice`);
}
}
let juice;
checkPantry(juice) // nothing will be printed
juice = 'pineapple'
checkPantry(juice) // 'we have pineapple juice'
The if
statement accepts an expression, in JS, null
and undefined
will be false so we can easily check to see if the value exists.
If you are treating null
and undefined
as different things in your program, you could explicitly check the value like this:
const checkPantry = (juice) => {
if (juice === null) {
console.log('we ran out of juice');
} else if (juice === undefined) {
console.log('not sure if there is any juice')
} else {
console.log(`we have ${juice} juice`);
}
}
let juice;
checkPantry(juice) // 'not sure if there is any juice'
juice = 'pineapple'
checkPantry(juice) // 'we have pineapple juice'
juice = null
checkPantry(juice) // 'we ran out of juice'
Generally, null
and undefined
can be treated similarly for values, so it’s not necessary to check this explicitly. Let’s have a look at another useful feature which can help in more complicated cases.
Optional chaining
When dealing with objects, there are times when you want a property but don’t know if it exists. In the past, this could be annoying when looking deeply into an object but optional chaining was added and has full support across all major browsers, and has done for some time.
Let’s have a look at some different examples.
Properties
Accessing a property of an object will be undefined
it doesn’t exist, but if you access a property on undefined
, you’ll receive a TypeError
— optional chaining means we can use the ?.
to attempt to access a nested property and return undefined
if ID doesn’t exist.
const shelf = {
top: {
plate: 3
},
bottom: {
can: 8,
chest: {
letter: 2
}
}
}
}
shelf.middle // undefined
shelf.top.chest.letters // Throws a TypeError exception
shelf.top?.chest?.letters // undefined
This is much nicer than the alternative in the past, which involved defensive checks to figure out if a nested value could be accessed.
Expressions
Another useful case for optional chaining comes from the []
accessor syntax, this can be useful with objects and arrays.
const ids = [1,2,3]
ids[1] // 2
ids?.[1] // 2
let names;
names[0] // Throws a TypeError exception
names?.[0] // undefined
This makes it much easier to deal with dynamic values without having to check the values before attempting to access a property.
Functions
Finally, optional chaining works with functions too. Especially useful in JavaScript, as you can treat functions like values. This cleans up additional checks like with callbacks like this:
function makeCoffee(onDone) {
console.log('grinding')
console.log('prepping')
console.log('brewing')
if (onDone) {
onDone()
}
}
Now becomes this:
function makeCoffee(onDone) {
console.log('grinding')
console.log('prepping')
console.log('brewing')
onDone?.()
}
Optional chaining is useful syntax sugar which saves some typing and often makes logic easier to read through and understand. You’re much less likely to run into a TypeError
using optional chaining.