Flow is a static type checker. It detects inconsistent types in your code and you can add explicit type declarations in it via annotations.
-
In order for Babel to understand and remove Flow annotations during the transpilation process, install the Flow preset for Babel by running
yarn add --dev babel-preset-flow
. Then, add"flow"
underbabel.presets
in yourpackage.json
. -
Create an empty
.flowconfig
file at the root of your project -
Run
yarn add --dev gulp-flowtype
to install the Gulp plugin for Flow, and addflow()
to yourlint
task:
import flow from 'gulp-flowtype';
// [...]
gulp.task('lint', () =>
gulp.src([
paths.allSrcJs,
paths.gulpFile,
paths.webpackFile,
])
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError())
.pipe(flow({ abort: true })) // Add Flow here
);
The abort
option is to interrupt the Gulp task if Flow detects an issue.
Alright, we should be able to run Flow now.
- Add Flow annotations to
src/shared/dog.js
like so:
// @flow
class Dog {
name: string;
constructor(name: string) {
this.name = name;
}
bark(): string {
return `Wah wah, I am ${this.name}`;
}
barkInConsole() {
/* eslint-disable no-console */
console.log(this.bark());
/* eslint-enable no-console */
}
}
export default Dog;
The // @flow
comment tells Flow that we want this file to be typechecked. For the rest, Flow annotations are typically a colon after a function parameter or a function name. Check the documentation for more details.
Now if you run yarn start
, Flow will work fine, but ESLint is going to complain about that non-standard syntax we're using. Since Babel's parser is all up-and-running with parsing Flow content thanks to the babel-preset-flow
plugin we installed, it'd be nice if ESLint could rely on Babel's parser instead of trying to understand Flow annotations on its own. That's actually possible using the babel-eslint
package. Let's do this.
-
Run
yarn add --dev babel-eslint
-
In
package.json
, undereslintConfig
, add the following property:"parser": "babel-eslint"
yarn start
should now both lint and typecheck your code fine.
Now that ESLint and Babel are able to share a common parser, we can actually get ESLint to lint our Flow annotations via the eslint-plugin-flowtype
plugin.
- Run
yarn add --dev eslint-plugin-flowtype
and add"flowtype"
undereslintConfig.plugins
inpackage.json
, and add"plugin:flowtype/recommended"
undereslintConfig.extends
in an array next to"airbnb"
.
Now if you type name:string
as an annotation, ESLint should complain that you forgot a space after the colon for instance.
Note: The "parser": "babel-eslint"
property that I made you write in package.json
is actually included in the "plugin:flowtype/recommended"
config, so you can now remove it for a more minimal package.json
. Leaving it there is more explicit though, so that's up to your personal preference. Since this tutorial is about the most minimal setup, I removed it.
- You can now add
// @flow
in every.js
and.jsx
file undersrc
, runyarn test
oryarn start
, and add type annotations everywhere Flow asks you to do so.
One counterintuitive case is the following, for src/client/component/message.jsx
:
const Message = ({ message }: { message: string }) => <div>{message}</div>;
As you can see, when destructuring function parameters, you must annotate the extracted properties using a sort of object literal notation.
Another case you will encounter is that in src/client/reducers/dog-reducer.js
, Flow will complain about Immutable not having a default export. This issue is discussed in #863 on Immutable, which highlights 2 workarounds:
import { Map as ImmutableMap } from 'immutable';
// or
import * as Immutable from 'immutable';
Until Immutable officially adresses the issue, just pick whichever looks better to you when importing Immutable components. I'm personally going for import * as Immutable from 'immutable'
since it's shorter and won't require refactoring the code when this issue gets fixed.
Note: If Flow detects type errors in your node_modules
folder, add an [ignore]
section in your .flowconfig
to ignore the packages causing issues specifically (do not ignore the entire node_modules
directory). It could look like this:
[ignore]
.*/node_modules/gulp-flowtype/.*
In my case, the linter-flow
plugin for Atom was detecting type errors in the node_modules/gulp-flowtype
directory, which contains files annotated with // @flow
.
You now have bullet-proof code that is linted, typechecked, and tested, good job!
Back to the previous section or the table of contents.