We will completely ignore case 1, since as for right now, there is no easy way to expose type information for a single compiled file (except for writing a libdef file). In this article, we will focus on 2, how to make flow recognize types for files shipped in lib.

In our first step, we will install all necessary devDependencies to get our build chain going:

If you have already been using babel, then you are probably familiar with most of these dependencies. The babel plugin babel-plugin-transform-flow-strip-types will make sure that all flow related code will be stripped out in our compiled ES5 code.

Additionally, we install rimraf for running rm -rf commands and flow-copy-source to easily create so-called “flow-files”, which will be explained later on.

The most interesting part is, that all our tools are running on node so we don’t have to worry about any Windows / OSX / Linux development setups.

In our scripts section, we can find thebuild instruction, which will do three things:

npm run build:clean: Removes the current lib directory

npm run build:lib: Builds lib from scratch by compiling src via babel

npm run build:flow : Creates our flow-files, which are also put into lib

Step 3) is relevant for our goal… we call flow-copy-source which will (by default) copy all *.js files in src and copy them into the target directory lib , preserving the original directory hierarchy. Those files will have a different file-ending, called *.js.flow.

Side Note:I am using jest for testing, that’s why I added ignore rules for **/__tests__/** globs. I don’t want test files to land in lib.

Introducing “flow-files” (*.js.flow)

So if there is a file src/util/reduceChainPromises.js, the whole file will be copied to lib/util/reduceChainPromises.js.flow … but why do we need that?

Flowtype resolves modules the same way as node does. If you are importing my-lib/lib/util/reduceChainPromises, you would probably do it like this:

import reduceChainP from 'my-lib/lib/util/reduceChainPromises';

By default, flow will look into node_modules/my-lib and try to find the file lib/util/reduceChainPromises. But wait! Files in lib only contain pure ES5 code, so we loose our type information (flow will most likely treat reduceChainP as type any).

Luckily, if flow finds files with a *.js.flow file ending, it will prefer it over the actual *.js. So we vendor additional files, which will only tell flow what types are exposed in the accompanied ES5 file. Since these files are generated in our build process, we can always be sure that our flow-files are synced up with the actual code.

What about the file bloat?

You will probably argue that copying the whole src files to lib will unnecessarily bloat the resulting npm package, right? The flow team is aware of that issue and is working on an experimental command called flow gen-flow-files:

As soon as this feature is stable, we will be able to extract pure type information from our ES6 code, and put those type information in our *.js.flow, ultimately stripping the implementation code (kinda like the invert of what babel does to our src files).

Okay nice, so we now have a build system which will generate backwards compatible ES5 code AND add type definitions for our library, ready to be used by the consumers.

This was just a very basic introduction to this topic.

There are still a lot of open questions:

What about dependencies for our library?

What about dependency collisions with our library (e.g. lodash)?

What about different flow versions between the consumer & my library?

As soon as I find some time, I will do some follow up articles where we will deep dive into these problems and discover how to solve them as well!

You can check out our flow-support docs on styled-components to see how a more complex library is being typed and shipped with flow.

For questions or more updates on Flowtype leave me a tweet or follow me on Twitter @ryyppy