Who am I?

You can follow along here:

http://josepedrodias.com/presentations/flowtype-intro-requirelx.html

TOC

Overview

What is this Flowtype thing?

Flowtype is a static type checker for JavaScript.
It understands JS features from ES4 up to common ES7 features,
extending the language with type annotations.

Benefits

(once you have your code annotated)

see IDE features in nuclide's page

At what cost?

1 - flow-typed helps a lot
2 - flow check is your friend

When does it pay off?

Versus Typescript

What about TypeScript?

- Have you tried TypeScript?

- Isn't it more popular?

- Doesn't it provide all these features?

- Aren't the annotations similar?

Flowtype's function

TypeScript's function

Setup

Installation

suggested installation mode, project-wide:

npm install --save-dev flow-bin

setup (creates .flowscript file):

flow init

Usage

list files subject to analysis:

flow ls

validate whole project one-shot:

flow check

customize configuration:
Edit configuration, adding includes with .* RegExps around dirs such as node_modules.

Example .flowconfig

[ignore]
.*/node_modules/.*
 
[libs]
flow-libs/
 
[options]
unsafe.enable_getters_and_setters=true
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
suppress_comment=\\(.\\|\n\\)*\\#FlowExpectError

…Usage

Flowtype only type checks JS files with a flow comment such as: // @flow
To bypass whitelisting, check with the --all switch.

You can request flowtype to aid you in annotating a file with with:

flow suggest file_goes_here

Editor/IDE integration

A Tour

Built-ins

Flow has built-in support for the native types such as number, string, boolean, Function, RegExp, Date, Object, Array, Map, etc.

It knows the JS standard lib defs such as setTimeout().

It also knows the BOM API, DOM API, CSSOM API, Node.js API and React.

...Built-ins

any is a special type, superset and subset of all types (think *).

mixed also superset of all types, but not subset. This means you're required to cast to a regular type at its consumption. This is useful for function having very disparate possible return types.

Variables

let x = 3;
 
  // becomes
 
let x:number = 3; // type name or inline definition

Functions arguments

function name(v1, v2) {}
 
  // becomes
 
function name(v1:string, v2:?boolean):number {}
  // this function returns type number, 2nd arg is optional

Functions

// any function
function callLater(cb:Function) { … } 

// this way you're locking the function's signature
function callLater(cb:(ms:number)=>void) { … }

Object types and aliases

An object type is when you explicitly annotate with a literal obj:

function sayHi(guy:{name:string, age:number}):string { … }

One can define types for convenience (type aliases):

type Person = {name:string, age:number};
type ceteringue = string;
function sayHi(guy:Person):ceteringue { … }

Complex types

Can be annotated inline, in the class definition, if one exists:

class Employee {
  name              : string;
  dateOfBirth       : Date;
  yearsOfExperience : ?number;
  subordinates      : Array⧼Employee⧽;
  
  eat(food:string):void { … }
}

…Complex types

but can also be separately defined, mapped at attribution:

type Employee = {
  name              : string,
  dateOfBirth       : Date,
  yearsOfExperience : ?number,
  subordinates      : Array⧼Employee⧽,
  
  eat               : (food:string):void
}

Optional attributes

type Person = {
  name : string,
  age? : number
}

const x:Person = {name:'Anne'};

Without ? Flowtype would trigger an error at you if you forgot to pass the key age. This way it can be ommitted.

Polimorphic types (~ templates/generics)

Array⧼string⧽

Promise⧼string⧽
Promise⧼*⧽ // this means promise of you Flow infers by itself

{[key: string]: mixed}   // notation to typify hash objects
{[key: string]: string}  // we're limiting values to strings

Union types (~enumish/C union)

type Answer = 'yes' | 'no';
 
type StrOrStrArr = string | Array⧼string⧽;

Intersection types (~ implements)

type Answer = {name:string} & {age:number};
type PersonWithData = Person & {extraData:any};

Promises

Flow expects async returns to be typed as Promise⧼T⧽ where T is the return type.
You can't type the reject route as it is akin to an exception.

Async

async functions are supported and keep the Promise⧼T⧽ return type.
The mapping is such that:

Promise⧼T⧽  ⧼~⧽  async
then        ⧼~⧽  await
resolve     ⧼~⧽  return
reject      ⧼~⧽  throw

Drilling maybe types (nullables)

Flow follows the possibility of null/undefined in the code when that's possible,
either via disjoint string | null or ?string . You can prevent those by checking afterwards:

if (x == null)
  // or
if (x === null || x === undefined)

Drilling union types

handleChangeEmail: (event: SyntheticEvent) => void {
  if (event.target instanceof HTMLInputElement) {
    const email = event.target.value;
    this.processEmail(email);
  }
};

The if statement is required because value is not available on all event target elements, but it is for form elements such as the input.
Example

Some useful types

Function // any function
(a:string, b:number) => boolean
HTMLElement, Element, Node
Event

// in the react world...
SyntheticEvent
React.Element⧼*⧽ // any comp
React.Element⧼{prop1: string}⧽
React.Class⧼defProps, props, state⧽

Importing and exporting types

import type { Employee } from '../Employee';
 
export type Primitive = string | number | boolean;

Hint: if one imports symbols for Flowtype alone, better use import type than import as webpack doesn't support dependency loops and it doesn't get there.

Globals

declare var APP_VERSION:string;
declare var $:Function;

Useful when consuming external libs ad hoc, read closure-passed constants, etc.

External libraries

declare module Misc {
  declare var DEBUG: bool;
  declare function isLeapYear(year: string): bool;
  declare class Counter {
    val    : number;
    incr() : void;
  }
  declare type Response = 'yes' | 'no' | 'maybe';
}

Converting to flow

Troubleshooting

Troubleshooting - ignore next line

Add this line to.flowconfig

suppress_comment=\\(.\\|\n\\)*\\#FlowExpectError  

whenever you need to suppress hard flow issues, add a comment line prior to it:

type Abc = { name: string; }

function log(abc:Abc) {
  // #FlowExpectError
  console.log( abc.age );
}

Troubleshooting - casts

allow type-annotate expressions

( Expression : type )
(2 + 3 : number)

won't let you do cast to any type:

const num:number = ( 'a' : number);

you first cast it to any:

const num:number = ( ( 'a' : any ) : number);

this is the cast loopwhole, use sparingly!

Troubleshooting - weak mode

If you annotate a file with// @flow weak flow does a more relaxed analysis. Checks types on functions but instead of inferring assignments, keeps untyped attributions asany which leaves out some of the errors.

Troubleshooting - Remove whitelisting

If you're in a hurry, it may be temporarily viable to remove the // @flow comment out of the problematic file and properly type-annotate later.

Wrapping up

References

Image credits