Flow
static type checker for JavaScript
Tom MacWright @tmcw
Mapbox Studio
our project
250+ components, complex reducers, complex interactions. in production
Static Analysis Making judgments about source code and its behavior without actually running the source code.
Type Checking Confirms that you provide the right arguments and use operators with compatible types.
Philosophy Rule of Repair: When you must fail, fail noisily and as soon as possible.
// crashes sometimes
for (var i in j)
  try {
    var result = JSON.parse(j[i]);
  } catch (e) {
    console.warn('idk lol');
  }
But we usually follow the rule of repair Regardless of whether we call it that
Linters move syntax errors from the test stage to authoring stage
syntastic
Unit tests move logic errors from the production stage to test stage
mocha
Integration tests move logic errors from the QA stage to test stage
nightwatch
So we already do the rule of repair! The trick is thinking of failure on the time axis.
Flow detects type errors before code runs.
// js has types
typeof '' === 'string'
typeof {} === 'object'
typeof 1 === 'number'
// javascript often
// implicitly converts
// types

> 'number ' + 5
'number 5'
> 5 == '5'
true
JavaScript type comparison table omitted Most of the things people joke about in JS like type comparison, 'crazy' floating point math, etc., are part of the greater language family or lower-level assumptions. Language religiousity is dumb!
So, what does Flow do for me?
Let's start with eslint
eslint is like JSHint. You may remember them from nagging warnings about indentation and quotes. But look at no-undef
var b = a
var b = a
^
ReferenceError: a is not defined
Without running the code!
var me = { name: 'Tom' }

console.log(me.Name)
(⌣_⌣”)
Going beyond ReferenceError is harder. Enter Flow
/* @flow */
var me = { name: 'Tom' }
console.log(me.Name)
undef.js:3

3: console.log(me.Name)
                     ^^ property `Name`.
                     Property not found in
3: console.log(me.Name)
               ^^^^^ object literal
Using Flow: adding the /* @flow */ comment to the top of the file marks it as part of the Flow universe.
You can gradually add annotations to your project. That's what we did. We use are-we-flow-yet to track our progress.
The more files that are annotated, the more complete the system becomes.
Flow catches:
/* @flow */
// implicit typecasts
var casted = 'hello ' * 5

-------------

test.js:3
3: var casted = 'hello' * 5;
                ^^^^^^^ string. This type
                is incompatible with
3: var casted = 'hello' * 5;
                ^^^^^^^^^^^ number
/* @flow */
// bad arguments
function timesTwo(x: number): number {
  return x * 2
}

var casted = timesTwo('hello')
-------------

test.js:7
7: var casted = timesTwo('hello');
                ^^^^^^^^^^^^^^^^^ function call

7: var casted = timesTwo('hello');
                         ^^^^^^^ string. This type is 
                         incompatible with
3: function timesTwo(x: number): number {
                        ^^^^^^ number
/* @flow */
// module boundaries

var fs = require('fs')
fs.readFileSync(2)

-----

test.js:4
  4: fs.readFileSync(2);
        ^^^^^^^^^^^^ call of method `readFileSync`.
        Function cannot be called on

        declare function
        readFileSync(filename: string): Buffer;
                     ^^^^^^^^
                     intersection: function type(s).
Unsafe access to props in React This one's pretty crazy
PropTypes are runtime typechecking for React component props. In development mode, they cause an incorrect or missing prop to show a warning.
/* @flow */

// propTypes enforcement
var MyComponent = React.createClass({
  propTypes: {
    onClick: React.PropTypes.func
  },
  click() {
    this.props.onClick('hi')
  },
  render() {
    return (<button onClick={this.click}>
      hi
    </button>);
  }
});

test.js:10
 10:     this.props.onClick('hi');
         ^^^^^^^^^^^^^^^^^^^^^^^^ call of method `onClick`.
         Function cannot be called on possibly undefined value

  7:     onClick: React.PropTypes.func
                  ^^^^^^^^^^^^^^^^^^^^ undefined
But wait, there's more!
/* @flow */

var React = require('react');
var MyComponent = require('./test');

var App = React.createClass({
  render() {
    return (<MyComponent />);
  }
});

module.exports = App;

requirer.js:8
  8:     return (<MyComponent />);
                 ^^^^^^^^^^^^^^^ React element `MyComponent`

  6:   propTypes: {
       ^^^^^^^^^ property `onClick`. Property not found in.
                See: test.js:6

  8:     return (<MyComponent />);
                 ^^^^^^^^^^^^^^^ type parameter `Props` of React
                                 element `MyComponent`
Flow shines for refactoring: change a function to take a string instead of a number and instantly identify all the usages that need updating
Okay, so Flow is magic. But all power comes at a price
Type Annotations
function foo(x: number): number {}
Flow can't infer all types. Unless it's painfully obvious that a function needs a number, you're going to have to tell it.
Yet another JavaScript syntax extension: your eslint, babel, and node code is all going to have to deal with Flow annotations, unless you use comments.
Yet another JavaScript parser Flow supports a lot of fancy JavaScript syntax, but not 100%. Nothing really supports 100% anymore.
ES6 will soon be transpiler-free. Flow & JSX? Not so much.
No Windows support (yet). Written in OCaml, binaries available for OSX & Linux.
Versus TypeScript?
TypeScript has a huge library of annotations in the StronglyTyped organization
TypeScript is written in TypeScript, not OCaml
We chose Flow because of compatibility with Babel & React and its fast pace of development
Overall, Flow is A+! It is strict but fair.
Fin.
Mapbox
is hiring
I'm
Tom MacWright @tmcw