DEV Community

Zaid Rehman
Zaid Rehman

Posted on

Stage 4: Optional chaining

Motivation
When looking for a property value that's deep in a tree-like structure, one often has to check whether intermediate nodes exist or not like below

const street = user.address && user.address.street;
Enter fullscreen mode Exit fullscreen mode

Also, many API return either an object or null/undefined, and one may want to extract a property from the result only when it is not null

const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined
Enter fullscreen mode Exit fullscreen mode

The Optional Chaining Operator allows a developer to handle many of those cases without repeating themselves and/or assigning intermediate results in temporary variables:

var street = user.address?.street
var fooValue = myForm.querySelector('input[name=foo]')?.value
Enter fullscreen mode Exit fullscreen mode

The call variant of Optional Chaining is useful for dealing with interfaces that have optional methods

iterator.return?.() // manually close an iterator
Enter fullscreen mode Exit fullscreen mode

or with methods not universally implemented

if (myForm.checkValidity?.() === false) { // skip the test in older web browsers
    // form validation fails
    return;
}
Enter fullscreen mode Exit fullscreen mode

Syntax
The Optional Chaining operator is spelled ?.. It may appear in three positions:

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

Semantics
If the operand at the left-hand side of the ?. operator evaluates to undefined or null, the expression evaluates to undefined. Otherwise the targeted property access, method or function call is triggered normally.

a?.b                          // undefined if `a` is null/undefined, `a.b` otherwise.
a == null ? undefined : a.b

a?.[x]                        // undefined if `a` is null/undefined, `a[x]` otherwise.
a == null ? undefined : a[x]

a?.b()                        // undefined if `a` is null/undefined
a == null ? undefined : a.b() // throws a TypeError if `a.b` is not a function
                              // otherwise, evaluates to `a.b()`

a?.()                        // undefined if `a` is null/undefined
a == null ? undefined : a()  // throws a TypeError if `a` is neither null/undefined, nor a function
                             // invokes the function `a` otherwise
Enter fullscreen mode Exit fullscreen mode

Short-circuiting

a?.[++x]         // `x` is incremented if and only if `a` is not null/undefined
a == null ? undefined : a[++x]
Enter fullscreen mode Exit fullscreen mode

Stacking

a?.b[3].c?.(x).d
a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
// (as always, except that `a` and `a.b[3].c` are evaluated only once)
Enter fullscreen mode Exit fullscreen mode

Optional deletion

delete a?.b
a == null ? true : delete a.b
Enter fullscreen mode Exit fullscreen mode

Top comments (0)