Replacement for optional chaining for nested JS objects
09 Oct 2019 - Help improve this postSometimes you have to get a variable from a nested object like this
const customer = {
sources: {
data: [{
type: 'card',
last4: '1234'
}]
}
}
If you want to get the value 1234
, you could do this:
const { last4 } = customer.sources.data[0]
But if you don’t know if the whole object will be there (like with this Stripe response), you’ll need to check for every variable:
let last4
if (customer
&& customer.sources
&& customer.sources.data
&& customer.sources.data[0]
&& customer.sources.data[0].last4) {
last4 = customer.sources.data[0].last4
}
Or you could do a try...catch
:
let last4
try {
last4 = customer.sources.data[0].last4
} catch (error) {
// do nothing
}
But I like to have a little function that does this for me:
// Copy this function
const get = (object, selector, defaultValue = undefined) => {
if (typeof object !== 'object' || object === null || typeof selector !== 'string') return defaultValue
const value = selector.replace(/\[/g, '.[').split('.').reduce((prev, curr) => {
if ([undefined, null].indexOf(prev) > -1) return undefined
if (curr.startsWith('[')) return prev[curr.slice(1, -1)]
else return prev[curr]
}, object)
return (value === undefined) ? defaultValue : value
}
// So you can do
const { last4 } = get(customer, 'sources.data[0]')
There is a relevant proposal by tc39 (thanks @TehShrike).
Inspired by the Ember
get
-function
Happy coding! – Found a mistake or a typo? Please submit a PR to my GitHub-repo.