Our team have been using Flow types for some time at Red Badger and we’ve seen some real benefits. Namely, more robust code that is easier and safer to refactor, and significant productivity gains thanks to excellent tooling with the Nuclide IDE (thanks Facebook!).
Yesterday I watched an excellent Strangeloop talk on cl0jure.spec:
Stuart Halloway’s Talk on clojure.spec For those unfamiliar, it’s a specification language for the dynamically typed language Clojure. It allows developers to specify the shape of some data a function expects, and specify some invariants that should be satisfied before or after execution. It seems somewhat inspired by design by contractand there are some definite overlaps with the kind of guarantees provided by a type system.
However — the powerful idea behind clojure.specis that enforcement of these specifications is completely left up to the developer. Albeit aided by some built in utility functions. For this to work, the specifications must be available at runtime.
Statically typed languages commonly only make usage of type information at compile time. Yet, languages such as C# and Java have also had mechanisms for accessing type information at runtime, as part of a “ Reflection API”,for a long time .
Exposing Flowtypes at Runtime
Furthermore, users of Flow normally run their JS code through Babel, where flow-strip-typesremoves any annotations and outputs executable code. In order to retain type information, we can instead replace this plugin with flow-runtime-types,a small transform library I wrote for this purpose .
flow-runtime-typesexposes the AST of our Flow annotations to runtime code, we can then interpret this AST to build some interesting features.
The AST looks like this:
Quite simple to understand, and low level enough to build some tooling that takes advantage of it.
Type Conformance Validation
At the edges of any system, type checked or otherwise, it is necessary to implement validation logic to ensure data is in the correct shape. When using Flow, encoding these rules once as part of the type system, and again as part of executable code can feel redundant.
Instead, we could utilise the Flow annotations AST to produce automatic runtime validation of the data.
I chose to convert the flow types into a Joi schema object ( https://github.com/hapijs/joi) so validation can be handled by a battle tested library. For example, using the above type definitions:
This doesn’t mean we can remove all other validation logic, as there is no way to encode arbitrary constraints (e.g. the max length of a string) in Flow. It does however save us from the tedium of structural validation, so we can instead focus on validating the semantic meaningof the data.
I’ve implemented this as a proof of concept for primitive and generic types in Flow, and it seems to work effectively. We could certainly also use this approach to completely replace runtime propType validationin React components.
Generating test cases for property-based/unit tests
We can also use the Flow AST to generate valid fixtures to use in unit or property based tests. Usage as follows:
We can execute this function many times over to produce random, but valid, output for our system. We can then directly use this in property based testsor fuzz testing(as seen in tools such as https://github.com/carteb/carte-blanche) when applied to UI programming.
Limitations & Improvements
One limitation of this approach is that runtime introspection is only available for explicit type declarations.One of the major benefits of Flow is type inference. Addressing this limitation could take one of the following approaches:
Request typed AST information from Flow via the CLI with the ` — json` flag. This type of integration is used by Nuclide and other tools. The specific call needed to make this work doesn’t yet exist, but is being discussed at https://github.com/facebook/flow/issues/248.
Can you think of any other use cases for runtime type introspection? Or an easier way to achieve it?
A proof of concept is available here on GitHub, not yet distributed to NPM: