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)