Thanks to Hurricane Matthew raining down on Colombia, I’m forced to do something productive with my time. So naturally, I’ve been thinking about type systems, specifically Flowtype for JavaScript.

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!).

Without engaging in the static vs. dynamic typing debate, I believe gradual typing provides an excellent balance of confidence vs. flexibility. I’ve seen it work effectively on a large (and important) JavaScript codebase.

Inspiration

Yesterday I watched an excellent Strangeloop talk on clojure.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 contract and there are some definite overlaps with the kind of guarantees provided by a type system.

However — the powerful idea behind clojure.spec is 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.

To bring some of the ideas from core.spec to JS, I set about building a similar feature for Flowtyped JavaScript.

Exposing Flowtypes at Runtime

Flow is written in OCaml, not JavaScript, and type checking happens in the Flow process during development. This means type information is not accessible from JavaScript.

Furthermore, users of Flow normally run their JS code through Babel, where flow-strip-types removes 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-types exposes 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.

Use Cases

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 meaning of 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 validation in 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 tests or 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.

Compile Flow (OCaml) into JS. This is theoretically possible with a tool like https://github.com/ocsigen/js_of_ocaml and would open up many other integration opportunities, but seems like a lot of work. If possible, this would also mean that we could directly use Flow’s model of the type system rather than re-implementing our own weaker form in JavaScript.

Related Projects

Interesting?

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: