Flow Type for coding in JavaScript with guardrails

Car attempts to use JavaScript

Getting started

Getting fast feedback in your IDE is vital to having a happy developer experience. Flow adds that value for me for JavaScript programming.

Atom

Install ide-flowtype. I just switched from the lighter-weight linter-flow and I’m liking it.

The good parts

  • You can use Flow with no transpilation
  • You can use Flow with no transpilation
  • The documentation it provides is very helpful. It’s more rigorous than JSDoc, but less human friendly because there’s no description. You’ll need an old fashioned comment (JSDoc?) for that.
  • Speeds dev time. The fast feedback, jump to definition, and documentation makes it easier to do work (guardrails)
    • Even with weak mode and lazy annotations (just using primitive types instead of fully specifying), you get very useful feedback as you code
  • Flow updates regularly with improvements
  • Flow is a Facebook thing, so the integration with React is great, even if it changes drastically. This happened to me and tool they distributed to upgrade syntax worked great
  • You support one evil mega corporation over another evil mega corporation
  • You can use Flow with no transpilation

Criticisms

  • For “gradual” typing, Flow type is heavy handed (but getting better).
    1. “weak” mode might be deprecated. “It makes incremental adoption of flow much easier for those of us who are attempting to add it to a codebase that was largely written without the guard rails that typing provides.” UPDATE: weak mode isn’t going away anytime soon
    2. It’s supposed to infer type, but it feels not that great. It’s not as good as humans. Flow gets especially annoying with object destructuring.
  • Objects have to be fully spec’ed out or it’s an error, for example, you can’t add arbitrary properties to an Object. UPDATE: the documentation about optional properties is much better UPDATE2 They call these sealed vs unsealed UPDATE3 They’re going to transition to “exact objects by default“.

    const a = {
      foo: 'hi'
    };
    // property `bar` Property not found in object literal
    a.bar = 'uh oh';
    // you're supposed to do this instead
    const b: {foo: string, bar?: string} = {
      foo: 'hi'
    };
    b.bar = 'well ok';
  • Flow has no mechanism for turning off or ignoring some kinds of errors besides the $FlowFixMe suppression comment, and Flow can’t infer everything so your’e gonna either add a lot of $FlowFixMes or defensive statements.
  • .flowconfig is not well documented
  • The Flow binary is extremely finicky. I’ve caught it stealing all my CPU. It’s slow. In the Atom integration, you have to run code through twice to before you see an error. Which means if you fix something, you won’t get feedback until your seconds save. UPDATE: This gets better with every release, but it’s still really fragile. The first flow check can take 30 seconds to run while it warms up, even longer with monorepos.
  • The syntax consumes a lot of lines, and results in very long lines because you have to describe large objects on one line. Especially since I only use comment syntax.
  • Flow comment syntax is poorly documented. All the literature assumes you’re using the syntax that requires a build step to remove Flow annotation.
  • If you’re not using Flow comment syntax, all your annotations are being stripped in the artifact. Meaning consumers of your package won’t see annotations.
  • If you’re defining your own types in a myLibDef.js, Eslint will go nuts. You have to install the flowtype plugin (see addendum).
  • Flow changes quickly and is pre 1.0, so there are frequent, breaking syntax changes
  • For example, in Flow 0.55, libraries that used Bluebird started erroring, and this has happened before
  • Flow will often throw stupid errors, requiring $FlowFixMe comments often
  • The https://github.com/flowtype/flow-typed/ ecosystem for third party type definitions is a trainwreck

TypeScript

I initially chose Flow because TypeScript required a transpilation step. Since then, TypeScript has come out with a comment syntax based on JSDoc, but it seems to be extremely limited. https://github.com/Microsoft/TypeScript/wiki/JSDoc-support-in-JavaScript. I read a lot of TypeScript but don’t write it, and it still feels ham-fisted compared to Flow. I especially don’t like how browsing TypeScript in GitHub, it’s hard read business logic when over half the lines are type definitions.

Best practices with Flow

  1. Use comment syntax
  2. Start very general, then gradually refine your types to be more specific

Addendum

eslintrc.js
{
    // Allow underlines and Flow comment syntax
    'spaced-comment': ['error', 'always', {exceptions: ['/'], markers: [':', '::']}],
  plugins: [
     ...,
     'flowtype'
   ],
  rules: {
    'flowtype/define-flow-type': 1,
  }
}
.flowconfig
[include]
src/
[ignore]
.*/node_modules/<add an entry for every problematic package, don't ignore all node_modules>

Further reading

Flow was version 0.86.0 as of writing

Leave a Reply

Your email address will not be published. Required fields are marked *