Today I learned September 11, 2019 3 min read — js

Optional chaining

If you'd ask me which lodash method I use most frequently, I'd probably say _.get. It's super convenient for reading nested objects safely, while also defining fallback values if the requested value doesn't exist. For everyone who never used _.get, here's how it works:

const foo = {
  bar: "foobar",
};

console.log(_.get(foo, "bar")); // = foobar

console.log(foo.hello.world); // throws exception, cannot read `world` of undefined
console.log(_.get(foo, "hello.world")); // = undefined

console.log(_.get(foo, "hello.world", "Some fallback")); // = some fallback

The fallback only gets applied if the requested value is undefined, so other falsy values will not unintentionally be overriden.

const foo = {
  bar: null,
};

console.log(_.get(foo, "bar", "Some fallback")); // = null

What is optional chaining

Optional chaining is a proposed API to deal with this problem natively in JS, instead of through libraries like lodash. It's been around for a while, but seemed to be pretty stale for most of it. So what changed? Well, it finally hit "stage 3". For those unfamiliar with the TC39 process, all proposals have to go through multiple stages on their way to become part of the JS standards. Stage 3 is generally considered an important step on that way, meaning the proposal is unlikely to change significantly after it hits this stage. Many developers, us at rexlabs included, use this stage to determine whether or not it is safe to use the proposed APIs (e.g. through babel plugins).

How does it work

So now that optional chaining hit stage 3, it is fairly safe to start using the new syntax. Since there is no browser support yet, you'll want to use the babel plugin as mentioned before. Here's how optional chaining works:

// Instead of
console.log(_.get(foo, "hello.world"));

// You can do
console.log(foo?.hello?.world);

This will not throw an exception, even if foo or foo.hello are undefined. It also works with methods:

const foo = {
  bar: "foobar",
};

console.log(foo?.hello?.world?.()); // = undefined

So how can I define proper fallbacks

This was one of the problems that probably caused this proposal to take so long to hit stage 3.

const x = foo?.hello?.world || "Some fallback";

The above would apply the fallback for any falsy value, not only if foo.hello.world is undefined. So to properly replicate lodash's behaviour you'd need to do something like this:

let x = foo?.hello?.world;
x = x === undefined ? "Some fallback" : x;

Looks pretty verbose and inconvenient. This is where the second proposal comes in: the nullish coalescing operator. Quite a mouth full, but in combination with optional chaining an amazing feature. It allows you do define fallbacks like so:

const x = foo?.hello?.world ?? "Some fallback";

So, compared to lodash, with these two proposals we don't need _.get anymore:

const foo = {
  bar: "foobar",
};

console.log(foo?.bar); // = foobar

console.log(foo.hello.world); // throws exception, cannot read `world` of undefined
console.log(foo?.hello?.world); // = undefined

console.log(foo?.hello?.world ?? "Some fallback"); // = some fallback

🎉