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;
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
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
The call variant of Optional Chaining is useful for dealing with interfaces that have optional methods
iterator.return?.() // manually close an iterator
or with methods not universally implemented
if (myForm.checkValidity?.() === false) { // skip the test in older web browsers
// form validation fails
return;
}
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
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
Short-circuiting
a?.[++x] // `x` is incremented if and only if `a` is not null/undefined
a == null ? undefined : a[++x]
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)
Optional deletion
delete a?.b
a == null ? true : delete a.b
Top comments (0)