idx: The Existential Function
At Facebook, we often need to access deeply nested values in data structures fetched with GraphQL. On the way to accessing these deeply nested values, it is common for one or more intermediate fields to be nullable. These intermediate fields may be null for a variety of reasons, from failed privacy checks to the mere fact that null happens to be the most flexible way to represent non-fatal errors.
Unfortunately, accessing these deeply nested values is currently tedious and verbose.
props.user && props.user.friends && props.user.friends[0] && props.user.friends[0].friends;
There is an ECMAScript proposal to introduce the existential operator which will make this much more convenient. But until a time when that proposal is finalized, we want a solution that improves our quality of life, maintains existing language semantics, and encourages type safety with Flow.
We came up with an existential function we call idx
.
idx(props, (_) => _.user.friends[0].friends);
The invocation in this code snippet behaves similarly to the boolean expression in the code snippet above, except with significantly less repetition. The idx
function takes exactly two arguments:
- Any value, typically an object or array into which you want to access a nested value.
- A function that receives the first argument and accesses a nested value on it.
In theory, the idx
function will try-catch errors that are the result of accessing properties on null or undefined. If such an error is caught, it will return either null or undefined. (And you can see how this might be implemented in idx.js.)
In practice, try-catching every nested property access is slow, and differentiating between specific kinds of TypeErrors is fragile. To deal with these shortcomings, we created a Babel plugin that transforms the above idx
invocation into the following expression:
props.user == null ? props.user : props.user.friends == null ? props.user.friends : props.user.friends[0] == null ? props.user.friends[0] : props.user.friends[0].friends;
Finally, we added a custom Flow type declaration for idx
that allows the traversal in the second argument to be properly type-checked while permitting nested access on nullable properties.
The function, Babel plugin, and Flow declaration are now available on GitHub. They are used by installing the idx and babel-plugin-idx npm packages, and adding “idx” to the list of plugins in your .babelrc
file.