How Optional Chaining Can Save You From Unexpected Errors in Javascript by@smpnjn

How Optional Chaining Can Save You From Unexpected Errors in Javascript

Optional chaining is a feature in Javascript which lets us access child properties of an object. It's used when properties are optional on an object, so that instead of returning an error, we still get a result from Javascript. If something is given an optional tag, then if it exists, the value will be returned - otherwise it will not throw an error. When to not use optional chaining, you should NOT use it to simply avoid errors. If you overuse it, you will make it more difficult to debug your code later.
image
Johnny Simpson HackerNoon profile picture

Johnny Simpson

Product, Engineering, Web


Optional chaining is a feature in Javascript which lets us access the child properties of an object, even if the parent object doesn't exist. It's used when properties are optional on an object, so instead of returning an error, we still get a result from Javascript.


Let's look at how it works.


Introduction to Optionality in Javascript

Anyone who has used Javascript for any length of time may have run into a pretty common issue where accessing a child property that is optional requires a lot of additional logic to test it exists before we do anything.


As an example, imagine an article building piece of software, where the object article may have a child property called relatedArticle, and within that, we may have some basic information on a related article.


Let's imagine we access a related article's URL by article.relatedArticle.url - however, not every article will have a related article. Traditionally in Javascript, assuming article is defined, we may have written something like this:


let article = {};
let url;
if(article.relatedArticle !== undefined) {
    url = article.relatedArticle.url;
}


Or we may be tempted to try something like this instead:


let article = {}
let url = article.relatedArticle ? article.relatedArticle : undefined;


This ultimately avoids the following error, should relatedArticle be undefined:


Cannot read properties of undefined (reading 'url')


If we have multiple child levels, though, we can end up writing quite a long string of undefined statements. This can get quite tedious and out of control for very big objects.


For example, below we try to access article.relatedArticle.url.rootDomain when we aren't really sure if these objects will be defined:


let article = {};
let rootDomain;
if(article.relatedArticle !== undefined && article.relatedArticle.url !== undefined) {
    rootDomain = article.relatedArticle.url.rootDomain;
}


Optionality solves this problem of excessive undefined checks. If something is given an optional ?. tag, then if it exists, the value will be returned - otherwise, it will not throw an error. In the above example, we don't even really need an if statement, anymore:


let article = {};
let rootDomain = article.relatedArticle?.url?.rootDomain;


By adding ?., should relatedArticle, or url be undefined or null, it will simply pass onto the next property without throwing an error. We can even improve this by setting a default value using nullish coalescing. Below, if the rootDomain property is found to be undefined, it will instead return https://google.com/:


let article = {};
let rootDomain = article.relatedArticle?.url?.rootDomain ?? 'https://google.com/';


How to use Optional Chaining in Javascript

In most examples, you can use optional chaining as described above, to avoid errors on truly optional properties within an object:


let article = {};
let rootDomain = article.relatedArticle?.url?.rootDomain;


One caveat to this is that you cannot add a question mark to the end of an object. The following, will, therefore, throw an error:


let article = {};
let rootDomain = article.relatedArticle?.url?.rootDomain?;
// Will throw an error like `Unexpected token ';'`


Although this is the most common way to use optional chaining, there are other ways to implement it in your code. Let's explore those too, to see the full extent of how to use optional chaining in your code and projects.


When to not use optional chaining

You should NOT use optional chaining to simply avoid errors. If you overuse optional chaining, you will silence errors and make it much more difficult to debug your code later. Instead, only use optional chaining for REAL optional properties!


Optional Chaining with Functions

You can use optional chaining with functions that you expect may return objects. It works exactly the same as before, only we add ?. after the function - myFunction(...arguments)?.x:


let myFunction = (x, y, z) => {
    if(z !== undefined) {
        return {
            x: x,
            y: y,
            z: z
        }
    }
}

let arguments = [ 1, 2, 3 ];

let getX = myFunction(...arguments)?.x;
console.log(getX); // Returns 1;


It's good to know at this stage that by default if a function has no return, it returns undefined - so this can be quite useful in situations where you are not sure a function will return anything at all.


Optional Chaining with Arrays

Optional chaining can also be used with arrays. For example - if you aren't sure if a property will be defined, but it contains an array. Below, a user can have an array of valid addresses, and we want to get the first line of their address, from their first address. Since it may not always exist, we can use optional chaining here to try and recover it.


let myUser = {
    name: "John Doe",
    age: 155
}

let getAddress = myUser.addresses?.[0]?.first


In this case, addresses is not defined on myUser, so getAddress will simply return undefined. If we want to check a property that should be array, the notation is obj.prop?.[index], while if we want to check an array index that may not exist, but has child elements within, the notation is obj[index]?.prop.


Optional Chaining with Optional Functions

Sometimes, the return of an object property may be a function. In these cases, we may want to run this function, but since it isn't always defined, we only want it to fire if it's there. In this case, we can use the notation ?.() to run the function, should it exist.


Consider the following example:


let myObject = [
    {
        id: 15,
        adder: (x) => {
            return x + 10;
        }
    },
    {
        id: 145,
    }
]

myObject.forEach((item) => {
    let runFunction = item.adder?.(10) || 10;
    console.log(runFunction);
});


Above, we have an object which contains an array, where each object contains an id - id, and may also contain the function adder. In this example, we want to run this adder function if it exists, or default the value to 10 if the function is undefined - so we run the function:


let runFunction = item.adder?.(10) || 10;


Here, if adder is defined, it runs, so we get x + 10 or 20, for the first element of the array, and simply 10 for the second, since adder does not exist.


Conclusion

Optional Chaining is incredibly useful and widely supported (excluding Internet Explorer). It can be used with pretty much any combination of property, array, or function, depending on return types.


For example:


  • obj?.property
  • obj?.[property]
  • obj[index]?.property
  • obj?.(args)
  • func(args)?.property


Optional chaining is therefore a great way to simplify your code and gives syntax and meaning to real optional properties within your objects.



Also published here.

react to story with heart
react to story with light
react to story with boat
react to story with money
L O A D I N G
. . . comments & more!