Optional Chaining with JavaScript and TypeScript

Alex Pickering
Level Up Coding
Published in
5 min readApr 7, 2020

--

We have probably all come across the following problem before — an optional property in an object, that if it is present, needs to be used and if not present, then do something else or break.

Sometimes this can become frustrating when the objects are nested because we would need to ensure each property along the path is present before attempting to access the next nested property. Let us see how we can more easily determine if an optional property is present.

Optional Chaining

We now have an exceptionally useful operator in chaining, the ? operator allows us to do an inline check for whether or not the property being accessed exists. Let us see how this works in action.

const foo = {
bar: 'data'
}
console.log(foo?.bar) // 'data'
console.log(foo.a) // undefined

In the above simplistic illustration, we see that foo has a property of bar which is just a string. If we tried to access bar it would display the data. If we tried to access a property that doesn’t exist, it would display undefined .

This would be considered the expected outcome.

Let us look at a more complicated example next. Let us assume that we have a response that is nested several levels deep. How would the optional chaining help us in this case?

Let us assume that we are getting a response from an API request, this response will include a data property and may or may not include a meta property, depending on whether or not it was attached on the request. We could easily write checks for whether or not meta exists on the response object using optional chaining.

const response = {
data: {
id: 123
}
}
console.log(response.meta.requestId) // Uncaught TypeError: Cannot read property of 'requestId' of undefinedconsole.log(response?.meta?.requestId) // undefined

This can easily be used in a conditional to trigger different flows or perform some additional functionality.

if (response?.meta?.requestId) {
return {
...response.data,
requestId: response.meta.requestId // We know this exists
}
}
// Old method
if (response && response.meta && response.meta.requestId)

As we can see from the example above, we can accomplish exactly the same outcome, using fewer evaluations and less code. We no longer need to worry about those pesky & to ensure a property is present before accessing it.

Does This Work With Functions?

Well, yes. If we had a function that accepts an optional callback parameter, we could use optional chaining to ensure that it is present before attempting to make a call with it. Let us see this in action shall we?

function foo(data, callback) {
console.log(data)
callback?.(data)
}
const cb = () => console.log('called')foo('bar', cb)// 'bar'
// 'called'

As we can see above, if the callback parameter is defined, it will pass the data property to it and call it. If however the callback is not defined, then it will not be called.

Knowing this could be useful when overloading a function signature to allow optional callbacks in JavaScript. It is worth noting that TypeScript also allows us to define a parameter as ‘optional’ using the ? within the function signature

function foo(bar?: string) {} // 'bar' is optional here

When Will Optional Chaining Work?

Optional chaining will work whenever we need to ensure the presence of a property or function before attempting to access it and will prevent an error from being thrown if the property or function does not exist.

Be cautious though as if the requirement for the function is to throw an error when a property does not exist, then we will want to make sure that the missing property is handled correctly.

Let us take a quick look at our last example. We’ve seen how we can ensure that nested properties are present and that we can ensure functions have been defined, now what about some of JavaScript’s other features, such as Map ?

const method = () => console.log('bar')
const data = new Map()
data.set('foo', method)
data.get('foo')?.() // invokes the function IF present

In the example above, we can see that by using the ? operator, we can optionally call the function when foo exists. However, if foo does not contain a function, this will throw a ‘TypeError’.

NB: Optional chaining does no validation on the type of data, only on whether or not it’s present. All type validations should still be handled independently from this step. This just allows us to extract or access the data in a safe manner.

Bonus — This will work with TypeScript’s nullish coalescing as well. Nullish coalescing is an conditional expression which determines the presence of a value and returns it when present or falls back to a preset that we can provide.

You can read more about nullish coalescing here ←

const countryCode = person?.country?.countryCode ?? 'Unknown'

In the above example, we’re optionally chaining down to the ‘countryCode’ property on the person. We then use nullish coalescing as our conditional expression to determine whether or not the property is present and how to handle it. If it is present, then countryCode will be set to person.country.countryCode and if it is not set, countryCode will become unknown after the evaluation. Simple.

That’ll Be All Folks!

Here are some references to resources that may peak your interest should you wish to either learn more or begin to implement optional chaining in your codebase.

Medium Nullish Coalescing

MDN Optional Chaining
MDN Nullish Coalescing
TypeScript Optional Chaining
TypeScript Nullish Coalescing

Thank you for reading, I hope you enjoyed and learned something. If you happen to have any feedback, criticism or contributions, feel free to jot them down in the comment section below.

Auf Wedersen!

--

--

Lead software engineer, predominantly working with TypeScript, Node and deploying into GCP or AWS with experience in the FinTech industry as well.