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.