Nullish Coalescing and Optional Chaining in JavaScript
When working with data, situations arise where you aren’t sure that the property you need exists. To build fault-tolerant applications despite issues like this, default values are provided to make apps fail gracefully. Before now, the logical OR (||) operator is used to achieve this. It is a syntax for selecting the second defined value or variable (default value) if the first value or variable is falsy (false, 0, ' ', null or undefined).
The logical OR (||) operator
The tenary condition a ? a : b
is shortened to a || b
. This concept is known as short-circuiting.
console.info(null || 'default'); //'default'
console.info(undefined || 'default'); //'default'
console.info(false || 'default'); //'default'
console.info(' ' || 'default'); //'default'
console.info(0 || 'default'); //'default'
Note that the default value is also used when the actual value is false, 0 or ' '. This is due to JavaScript evaluating these values as falsy. The operand on the left hand side was coerced to a boolean for the evaluation and any falsy value (false, 0, ' ', null or undefined) is not returned. This leads to unexpected results if these values are considered valid.
To get a better grasp, let's consider the function:
function getUser(user) {
return user.name || 'guestUser';
}
function isUserValid(user) {
return user.isValid || true;
}
const users = [
{name: 'Ethel', age: 25, isValid: true},
{name: '', age: 22},
{age: 18, isValid: false}
];
users.map(user => getUser(user));
// Expected output: ["Ethel", "guestUser", "guestUser"];
users.map(user => isUserValid(user));
// Expected output: [true, true, true];
If the expected value for the second object in the array above is ' ', the logical OR (||) operator nullifies it. A recent operator (??) has been added to JavaScript to address unexpected behaviours like this when using the || operator.
The nullish coalescing (??) operator
As opposed to the logical OR (||) operator, the nullish coalescing (??) operator provides a syntax for selecting the second operand only when the first operand evaluates to either null or undefined (but no other falsy values).
The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.
The tenary condition (a !== null && a !== undefined) ? a : b
is shortened to a ?? b
.
console.info(null ?? 'default'); // 'default'
console.info(undefined ?? 'default'); // 'default'
console.info(false ?? 'default'); // false
console.info(' ' ?? 'default'); // ' '
console.info(0 ?? 'default'); // 0
Take a look at the last three operations, therein lies the difference between || and ??. Actual values are returned for the two falsy values.
Rewriting the same function,
function getUser(user) {
return user.name ?? 'guestUser';
}
function isUserValid(user) {
return user.isValid ?? true;
}
const users = [
{name: 'Ethel', age: 25, isValid: true},
{name: '', age: 22},
{age: 18, isValid: false}
];
users && users.length > 0 &&
users.map(user => getUser(user));
// Expected output: ["Ethel", "", "guestUser"];
users && users.length > 0 &&
users.map(user => isUserValid(user));
// Expected output: [true, true, false];
Optional Chaining (?.)
The optional chaining operator (?.) provides a concise way of accessing the value of a property that is deep within a chain of connected objects without having to explicitly validate the reference. It is useful when accessing a property of an object which may be nullish (null or undefined).
Instead of causing an error when a reference is nullish, it short-circuits with a return value of undefined.
Given the object,
const user = {
name: 'Eve',
state: {
name: 'Lagos',
city: {
name: 'Gbagada',
code: 234
}
}
};
To access a property on this object that its availability is uncertain, we usually use the syntax below:
if(user.state && user.state.city) {
const stateCode = user.state.city.code;
}
This ensures that the program does not throw an error message such as:
TypeError: Cannot read property 'code' of undefined
With the newly introduced optional chaining operator (?.), you can check for data availability using a more concise approach.
console.info(user.state?.city?.code);
Given the array,
const users = [
{
name: 'Nora',
state: {
city: {
name: 'Accra',
code: '233',
},
},
},
{
name: 'Terrence'
},
{
name: 'Nick',
state: {
},
},
];
const cityCode = users?.length > 0 &&
users.map(user => user.state?.city?.code ?? 0);
// Expected output: ["233", 0, 0];
Note that when ?? is used together with || or && operator in an expression, always enclose the expression within parentheses due to its low value on the precedence table.
Given the object,
let user = {
name: 'Eve',
state: {
name: null
}
};
const stateValueWithError = user.state &&
user.state.name ?? 'Accra';
// Uncaught SyntaxError: Unexpected token '??'
console.info(stateValueWithError);
// Uncaught ReferenceError: stateValueWithError is not defined
const stateValueWithoutError = (user.state &&
user.state.name) ?? 'Lagos';
console.info(stateValueWithoutError); // "Lagos"
Conclusion
The main difference between the logical OR (||) and the nullish coalescing operator (??) is that || returns the first truthy value while ?? returns the first defined value. This becomes important when other falsy values are considered as valid values.
Though this feature is appealing to use, minimize its usage at this stage due to the cross-browser stability.