Optional Chaining in TypeScript using Custom Code Transformers

Improving compatibility and performance of ts-optchain

Neville Bowers
Inside Rimeto
4 min readMar 18, 2019

--

Update: since writing this post, ts-transform-optchain has been merged into ts-optchain to provide a unified solution to optional chaining in TypeScript.

Background

Several months ago we wrote about Rimeto’s approach to implementing Optional Chaining in TypeScript:ts-optchain. As we discussed, this package has been indispensable to our safely and concisely traversing tree-like structures in NodeJS environments.

However, because ts-optchain depends on object Proxying — a JavaScript feature introduced in ES2015 that cannot be pollyfilled — the package does not support all JavaScript execution environments. In particular, the lack of Proxy support in both IE11 and ReactNative Android* made ts-optchain a non-starter for integration into several of our client codebases. At the same time, we were increasingly facing the challenge of managing nullable GraphQL types safely with the growing intricacy of our client applications.

We needed a better solution.

A More Flexible Approach

We had separately started writing compile-time TypeScript code transformers to automate redundant or tedious development-time tasks for the team. Given our success using ttypescript to incorporate these code transformers into our TypeScript build process, we recognized the opportunity to re-write ts-optchain to take advantage of our established code-transformation pipeline and to remove the dependency on Proxy altogether.

Introducing ts-transform-optchain! Like it’s predecessor, ts-transform-optchain:

  • Uses a syntax that closely mirrors chained property access
  • Offers a concise expression of a default value when traversal fails
  • Enables IDE code-completion tools and compile-time path validation

However, rather than using Proxy to emulate optional chaining, ts-transform-optchain implements a TypeScript custom code transformer to convert TypeScript code taking the form:

…into the following compiled JavaScript code:

Because the transformed code relies only on primitive JavaScript operators, the code is compatible with any JavaScript execution environment, including IE11 and older versions of ReactNative Android!

Performance Wins

As a bonus, the transformed Optional Chaining code performs significantly faster than its Proxy-based equivalent. Using JSBench.me to compare performance of the Proxy vs transformer-based benchmark:

We observed the following performance improvements between the two implementations:

The benchmark suggest that removing the dependency on Proxy can improve the runtime performance of ts-optchain by 2 or 3 orders of magnitude! Of course, mileage will vary based upon specific use-cases and JavaScript execution environments.

Example: Traversing GraphQL Response Structures in React

When we use GraphQL in our React and ReactNative client applications, we must anticipate encountering nulls at any intermediate position in the response structure. For example, if we have the GraphQL query:

For us to render the first contact option for the employee’s manager safely, we must expect that any of employee, manager, contactOptions, etc. can be null or otherwise risk encountering a runtime TypeError when traversing the response. Using ts-transform-optchain we can now much more concisely handle the possibility of intermediate nulls. To illustrate:

Conclusion

No good solution exists (yet) in TypeScript for safely traversing nullable tree-like structures. While we wait for a better built-in solution, ts-transform-optchain is Rimeto's approach to Optional Chaining that preserves all the benefits of the TypeScript compiler while at the same time generating code operable across a broad range of JavaScript execution environments.

References

* JavaScriptCore for Android was just upgraded in RN 0.59 release on 3/12/2019. The upgraded JVC now includes support for Proxy (among other significant improvements)!

Love TypeScript as much as we do? Come work with us!

--

--