JavaScript developers constantly seek ways to write cleaner, more robust code. One common pitfall is dealing with potentially null or undefined values when accessing nested object properties. The absence of robust error handling can lead to unexpected TypeError exceptions, halting script execution. Fortunately, modern ECMAScript (ES6+) offers elegant solutions for NULL-safe property access and conditional assignment. These features significantly improve code readability and prevent common errors, making your JavaScript applications more reliable and easier to maintain. This article dives deep into these techniques, providing practical examples and explaining how to leverage them effectively in your projects.
Understanding the Problem: TypeError and the Need for NULL-Safe Access
Before ES2020, accessing properties of potentially null or undefined objects in JavaScript was a recipe for disaster. Consider a scenario where you’re trying to access user.address.street. If user or user.address is null or undefined, JavaScript would throw a TypeError: Cannot read property ‘street’ of null (or undefined). This error would abruptly stop your code, potentially leading to a poor user experience. Traditional approaches involved verbose and nested if statements to check for the existence of each property, making the code difficult to read and maintain. This is where the concept of NULL-safe property access becomes essential, allowing developers to navigate potentially missing data structures without causing errors.
The traditional approach often involved a series of nested ternary operators or if statements. For example:
const street = user && user.address && user.address.street ? user.address.street : 'Unknown';
This code, while functional, is difficult to read and scales poorly with deeper object structures. Imagine having to check multiple levels of nesting. The result would be a complex and error-prone chain of conditional checks. It’s easy to miss a check, leading to the dreaded TypeError. The optional chaining operator solves this problem elegantly, improving both the readability and maintainability of your code. According to a Stack Overflow survey, these errors are among the most common issues faced by JavaScript developers, highlighting the importance of NULL-safe approaches. Source: Stack Overflow Blog
The Optional Chaining Operator (?.)
ES2020 introduced the optional chaining operator (?.), a game-changer for NULL-safe property access. This operator allows you to access nested object properties without explicitly checking for null or undefined at each level. If any property in the chain is null or undefined, the expression short-circuits and returns undefined instead of throwing an error. This significantly simplifies code and makes it more resilient to missing data. This is particularly useful when dealing with data from external APIs or user input where the structure might not always be guaranteed. The ?. operator provides a clean and concise way to handle these scenarios.
Here’s how it works. Instead of writing:
const street = user && user.address && user.address.street;
You can use the optional chaining operator:
const street = user?.address?.street;
If user is null or undefined, user?.address will evaluate to undefined, and the entire expression will short-circuit, returning undefined. Similarly, if user exists but user.address is null or undefined, the same thing happens. Only if both user and user.address exist will user.address.street be evaluated. The featured snippet optimized paragraph is:
The optional chaining operator (?.) in JavaScript allows you to safely access nested object properties without causing errors if an intermediate property is null or undefined. If a property in the chain doesn’t exist, the expression short-circuits and returns undefined. This simplifies code and prevents TypeError exceptions, making your applications more robust.
- Reduces verbosity and improves readability.
- Prevents TypeError exceptions.
- Makes code more resilient to missing data.
The Nullish Coalescing Operator (??)
While the optional chaining operator handles null and undefined values gracefully, sometimes you need to provide a default value when a property is missing. That’s where the nullish coalescing operator (??) comes in. This operator returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand. This is different from the logical OR operator (||), which returns the right-hand side operand for any falsy value (e.g., 0, ‘’, false, null, undefined).
Consider the following example:
const street = user?.address?.street ?? 'Unknown';
In this case, if user?.address?.street evaluates to null or undefined, the street variable will be assigned the value ‘Unknown’. However, if user?.address?.street exists and has a falsy value like ’’ (an empty string), the street variable will be assigned the empty string, not ‘Unknown’. This distinction is crucial when you specifically want to handle null or undefined values and not other falsy values. This operator is a powerful tool for providing default values in a concise and readable way.
The nullish coalescing operator is a valuable addition to JavaScript because it allows developers to differentiate between genuinely missing values (null or undefined) and falsy values that are intentionally present. This nuanced behavior makes your code more predictable and less prone to unexpected results. For instance, consider a scenario where a user’s age is optional. If the age is not provided, you might want to default to a specific value, but if the age is explicitly set to 0, you want to respect that value.
Conditional Assignment with Logical Operators
Conditional assignment is another important aspect of writing robust JavaScript code. The logical AND (&&) and logical OR (||) operators can be used to conditionally assign values to variables based on the truthiness of a condition. These operators are particularly useful when you want to avoid assigning a value if a certain condition is not met. Combined with optional chaining and nullish coalescing, they provide a powerful toolkit for handling potentially missing data and assigning values accordingly.
For example, you might want to update a user’s settings only if the user object exists:
user && (user.settings = newSettings);
This code ensures that user.settings is only updated if user is truthy (i.e., not null, undefined, 0, ‘’, false, or NaN). If user is falsy, the assignment is skipped. Similarly, you can use the logical OR operator to assign a default value if a variable is currently null or undefined:
const config = existingConfig || defaultConfig;
However, be mindful of the difference between || and ?? as discussed earlier. The || operator will consider any falsy value, while ?? only considers null or undefined. Therefore, choose the operator that best suits your specific needs.
- Use ?. for NULL-safe property access.
- Use ?? to provide default values for null or undefined.
- Use && for conditional execution based on truthiness.
Practical Examples and Use Cases
Let’s look at some practical examples of how to use these techniques in real-world scenarios. Imagine you’re working with a user profile object retrieved from an API. The API might not always return all the fields, especially for optional information like social media profiles.
Here’s how you can safely access the user’s Twitter handle:
const twitterHandle = user?.profile?.twitter ?? 'Not Available';
This code elegantly handles the case where the user object, the user.profile object, or the user.profile.twitter property is missing. If any of these is null or undefined, the twitterHandle variable will be assigned the value ‘Not Available’. Another example is handling deeply nested configuration objects. Suppose you have a configuration object with nested settings for different modules.
const apiEndpoint = config?.modules?.auth?.apiEndpoint ?? 'https://default-api.com';
This code safely retrieves the API endpoint from the configuration object, falling back to a default endpoint if the configuration is missing or incomplete. These examples demonstrate the power and flexibility of optional chaining and nullish coalescing in simplifying complex data access scenarios.
FAQ
- What is the difference between || and ???
- The || operator returns the right-hand side operand if the left-hand side operand is any falsy value (0, '', false, null, undefined), while ?? only returns the right-hand side operand if the left-hand side operand is null or undefined.
- Can I use optional chaining with function calls?
- Yes, you can use optional chaining to safely call functions that might not exist. For example: user?.greet?.(). If user or user.greet is null or undefined, the function will not be called, and the expression will return undefined.
- Is optional chaining supported in all browsers?
- Optional chaining and nullish coalescing are supported in all modern browsers. However, older browsers might require transpilation using tools like Babel to ensure compatibility. [Check browser compatibility here.](https://caniuse.com/?search=optional%20chaining)
Question & Answer :
Is there an operator that would allow the following logic (on line 4) to be expressed more succinctly?
Notes:
- If
value = query(x)?.valuewere used, it would assignundefinedtovalue - There is also the
??=operator, however it isnยดt useful here,value ??= ...would only assign ifvalueis currentlynull/undefined - In CoffeeScript,
value = query(x).value if query(x)?.value?achieves the desired behaviour withouttry/catch, although it’s repetitive value = query(x)?.value ?? valueworks but isn’t conditional assignment, the assignment still happens, if we were setting anObject’s property the setter would be called unnecessarily. It is also repetitive- This logic cannot be abstracted into a function i.e.
value = smart(query(x), "value"), the assignment canยดt be made conditional that way
Keywords?
null propagation, existence operator
For some years now it is simply
a?.b?.c a?.b?.c ?? "default"
Check “Can I Use” for compatibility: https://caniuse.com/mdn-javascript_operators_optional_chaining,mdn-javascript_operators_nullish_coalescing
Update (2022-01-13): Seems people are still finding this, here’s the current story:
- Optional Chaining is in the specification now (ES2020) and supported by all modern browsers, more in the archived proposal: https://github.com/tc39/proposal-optional-chaining
- babel-preset-env: If you need to support older environments that don’t have it, this is probably what you want https://babeljs.io/docs/en/babel-preset-env
- Babel v7 Plugin: https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
Update (2017-08-01): If you want to use an official plugin, you can try the alpha build of Babel 7 with the new transform. Your mileage may vary
https://www.npmjs.com/package/babel-plugin-transform-optional-chaining
Original:
A feature that accomplishes that is currently in stage 1: Optional Chaining.
https://github.com/tc39/proposal-optional-chaining
If you want to use it today, there is a Babel plugin that accomplishes that.