The Problem of Property Chaining
All of us usually confronts the situation where an expected member of a property chain is undefined or null.
var user = { name: 'Joe' }
var zip = user.address.zip // ⚡⚡⚡Uncaught TypeError: Cannot read property 'zip' of undefined
In this example we expect user.address.zip
to exist but for some reason the address
is missing from the user
object.
Workaround #1: Logic Expressions
var user = { name: 'Joe' }
var zip = user
&& user.address
&& user.address.zip
Noisy and verbose.
Workaround #2: Ternary Operators
var user = { name: 'Joe' }
var zip =
user === undefined
? undefined
: user.address === undefined
? undefined
: user.address.zip
Even more noisy and verbose.
Workaround #3: Try-Catch
var user = { name: 'Joe' }
var ziptry {
zip = user.address.zip
} catch (error) {
zip = null
}
Breaks control and has own scopes.
Also a bit noisy.
Of course there are many workarounds, property selector libraries etc.
Optional Chaining Operator
There is a new proposal for ECMAScript. It is currently in stage 1 and there are 4 stages.
var user = { name: 'Joe' }
var zip = user?.address?.zip
Voilà, zip
is undefined
and does not throw an error.
The syntax supports function and constructor calls as well:
var address = getAddressByZip.?(12345)
If getAddressByZip
is a function it is called, otherwise, the expression is evaluated as undefined
.
Set Up Babel — Working Example
We need alpha 7 version from Babel.
npm install --save-dev babel-cli@7.0.0-alpha.19
npm install --save-dev babel-plugin-transform-optional-chaining@^7.0.0-alpha.13.1
Create a .babelrc
file in the project root:
{
"plugins": ["transform-optional-chaining"]
}
Create an index.js
to test the optional chaining operator.
var user = { name: 'Joe' }
var zip = user?.address?.zipconsole.log(zip)
Then run it:
node_modules/.bin/babel-node index.js
Or transpile the code:
node_modules/.bin/babel index.js
The Transpiled Code
Transpiled version of the index.js
above:
var _user$address;var user = {
name: 'Joe'
};var zip =
user == null
? void 0
: (_user$address = user.address) == null
? void 0
: _user$address.zip;console.log(zip);
The transpiled code is constructed of nested ternary operators similar to the code in workaround #3 above. The main difference is that this code is optimized for edge cases also.