26

I've been doing a lot of code likes this in javascript

if (params && params.profile && params.profile.address && params.profile.address.default)

where I have to check for each optional, it gets pretty tedious. Is there a better way in javascript that's similar to how swift would deal with optionals, e.g.

if let checked = params?.profile?.address?.default?

MonkeyBonkey
  • 46,433
  • 78
  • 254
  • 460
  • Possible duplicate of http://stackoverflow.com/questions/2631001/javascript-test-for-existence-of-nested-object-key ? – CodingIntrigue Nov 17 '15 at 13:19
  • 1
    It's coming! It's been moved to Stage 3 (as of late July 2019) https://twitter.com/drosenwasser/status/1154456633642119168 – Aurelio Jul 26 '19 at 10:03

6 Answers6

10

Optional chaining has now been added to the language using the same ?. syntax as Swift, so I recommend doing it that way unless you need to support older browser versions.

If you do, I've written a function that handles this:

function getSafe (func) {
    try {
        return func()
    } catch (e) {
        if (e instanceof TypeError) {
            return undefined
        } else {
            throw e
        }
    }
}

Call it like this:

if (getSafe(() => params.profile.address.default))

This works because by wrapping it in an anonymous function, it isn't evaluated until the try/catch block, which will then catch the error and return undefined if any of the parent properties are undefined.

Checking whether e is a TypeError prevents it from swallowing any other errors the function might throw so those can still be handled as needed. If you want it to just return undefined on any error, you can remove that part:

function getSafeNoErrors (func) {
    try {
        return func()
    } catch {
        return undefined
    }
}
John Montgomery
  • 6,739
  • 9
  • 52
  • 68
6

There is no such operator in native JS.

However, there is a Babel plugin that will accomplish that, please check https://github.com/davidyaha/ecmascript-optionals-proposal

Also, please refer to Null-safe property access (and conditional assignment) in ES6/2015 for more references

Community
  • 1
  • 1
Dokinoki
  • 171
  • 2
  • 10
4

2020 Answer, It Exists!!!

You can now directly use ?. (Optional Chaining) inline to safely test for existence. All modern browsers support it.

If a property exists, ?. proceeds to the next check, or returns the valid value. Any failure will immediately short-circuit and return undefined.

const example = {a: ["first", {b:3}, false]}

example?.a  // ["first", {b:3}, false]
example?.b  // undefined

// Dynamic properties ?.[]
example?.a?.[0]     // "first"
example?.a?.[1]?.a  // undefined
example?.a?.[1]?.b  // 3

// Functions ?.()
null?.()                // undefined
validFunction?.()       // result
(() => {return 1})?.()  // 1

// DOM Access
domElement?.parentElement?.children?.[3]?.nextElementSibling

If you do not check a case, the left-side property must exist. If not, it will throw an exception.

example?.First         // undefined
example?.First.Second  // Uncaught TypeError: Cannot read property 'Second' of undefined

?. Browser Support - 82%, Oct 2020

Node Support - 14+

Mozilla Documentation

Gibolt
  • 42,564
  • 15
  • 187
  • 127
1
function optionalChaining(obj, chain) {
  return chain
    .split('.')
    .reduce(function(acc, val) {
        return acc ? acc[val] : undefined;
    }, obj);
}

var user = {
 address: {
  street: 'No.969 West WenYi Road',
},
 a: { b: { c: 2 } },
}

optionalChaining(user, 'address.street'); // 'No.969 West WenYi Road'
optionalChaining(user, 'a.b.c') // 2

The function can simulate optional chaining.

Mervyn
  • 543
  • 4
  • 15
1

Yes, there's one. In ES2020 they've added optional chaining and optional function invocation.

   const person = {
        name: "Someone",
        age: 28
    }
    console.log(person?.name) // "Someone"
    console.log(person?.["age"]) // 28
    person?.getAge?.() //   no errors, just undefined
0

Just to add to the answer above you can now install this babel plugin straight from NPM:

https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

obj?.prop       // optional static property access
obj?.[expr]     // optional dynamic property access
func?.(...args) // optional function or method call

Notes:

In order to allow foo?.3:0 to be parsed as foo ? .3 : 0 (as required for backward compatibility), a simple lookahead is added at the level of the lexical grammar, so that the sequence of characters ?. is not interpreted as a single token in that situation (the ?. token must not be immediately followed by a decimal digit).

https://github.com/tc39/proposal-optional-chaining

Also worth checking out:

https://github.com/tc39/proposal-nullish-coalescing

https://github.com/babel/babel/tree/master/packages/babel-plugin-proposal-nullish-coalescing-operator

Jon Wood
  • 1,531
  • 14
  • 24