Facebook's static type-checker for JavaScript
Crosslead Engineering -- Nov. 6th, 2015
> a.definitelyHasThis.method()
TypeError: Cannot call method 'method' of undefined
at repl:1:22
at REPLServer.self.eval (repl.js:110:21)
at Interface.<anonymous> (repl.js:239:12)
at Interface.emit (events.js:95:17)
at Interface._onLine (readline.js:203:10)
at Interface._line (readline.js:532:8)
at Interface._ttyWrite (readline.js:761:14)
at ReadStream.onkeypress (readline.js:100:10)
at ReadStream.emit (events.js:98:17)
at emitKey (readline.js:1096:12)
> function multiply(a, b) { return a * b; }
undefined
> multiply('a', 'b')
NaN
The goal of Flow is to find errors in JavaScript code with little programmer effort. Flow relies heavily on type inference to find type errors even when the program has not been annotated - it precisely tracks the types of variables as they flow through the program.
/* @flow */
function foo(x) {
return x * 10;
}
foo('Hello, world!');
hello.js:7
7: foo("Hello, world!");
^^^^^^^^^^^^^^^^^^^^ function call
4: return x*10;
^ string. This type is incompatible with
4: return x*10;
^^^^ number
Flow's type checking is opt-in: files are not type checked unless you ask it to.
// no flow comment, no more errors!
function foo(x) {
return x * 10;
}
foo('Hello, world!');
(primatives)
any // -> Describes any possible type
boolean // -> Describes true or false values
mixed // -> Describes the supertype of all types
number // -> Describes literal number values
string // -> Describes literal string values
void // -> Describes undefined values
(Built-in Class Types)
Array<T> // -> Describes Array objects with elements of type T
Boolean // -> Describes Boolean objects (but not boolean literal values!)
Class<T> // -> Describes the type of the class that would instantiate a "T" instance
Function // -> Describes any function
Number // -> Describes Number objects (but not number literal values!)
Object // -> Describes any object
String // -> Describes String objects (but not string literal values!)
Promise<T> // -> Describes Promise resolving to type T
(Signature Types)
// Object Literal Signature Type
{ prop1: number, prop2: string } // two specific properties
{ [key: string]: string } // only string values, any keys...
// Function Signature
(p: string, x: number) => number
// callable object signature
{ (p: string): number, prop: number }
// Tuple
[number, string, number]
(Algebraic Types)
// type unions
type myPrimative = string | number | boolean
// type intersections
type fooBarFn = ((x: Foo) => void) & ((x: Bar) => void)
(building your own types!)
// lets make a binary tree type!
// the binary tree type is parametric to the type of its leaves "T"
type binTree<T> = {
left?: binTree<T> | T,
right?: binTree<T> | T
}
// this typechecks ok!
const tree: binTree<number> = {
left: 1,
right: {
left: 1
}
};
// this does not!
const tree: binTree<number> = {
left: 1,
right: {
left: 's' // string does not match the given type parameter
}
};
(More examples...)
type stringOrNumber = string | number;
type nodeCallback = (error?: any, data: any) => void;
//
// a few types used in app/global/common/ScoreToggle.js
//
type Score = { [key: string]: number | Score };
type Node = {
_id: string,
keyPath?: string,
_rId?: string,
scores?: Score,
parent?: Node
};
type NodeList = Array<Node>;
type ScoreMap = { [key: string]: Score };
/* @flow */
function foo(x) {
return x * 10;
}
foo('Hello, world!');
Add a comment to start typechecking a file...
// variable
var a: number = 1;
// class
class A {
prop: number // instance property annotation
static sProp: string // static property annotation
constructor(a: number) { // no type annotation needed on constructor
this.prop = a;
}
method(): A { // method annotation, returning class A as type
return this;
}
}
// function parameter and return type annotation
async function getTypes(organizationId: string): Promise<Array<string>> {
const types = await get(`api/communications/types/${organizationId}`) || [];
return types;
}
// parametric polymorphism (or templates / generics)
function makeTuple<T>(a: T, b: T): Array<T> {
return [ a, b ];
}
Type Annotations
declare class _D3_ELEMENT_ {
select(key: string): _D3_ELEMENT_;
selectAll(key: string): _D3_ELEMENT_;
attr(config: { [key: string]: any }): _D3_ELEMENT_;
attr(key: string, value: any): _D3_ELEMENT_;
attr(key: string): any;
style(config: { [key: string]: any }): _D3_ELEMENT_;
style(key: string, value: any): _D3_ELEMENT_;
style(key: string): any;
}
declare class _D3_ {
select(key: string): _D3_ELEMENT_;
selectAll(key: string): _D3_ELEMENT_;
format(spec: string): Function;
scale(): Function;
svg(): Function;
time(): Function;
extent(data: Array<any>, accessor?: Function): Array<any>;
values(obj: {[key: string | number]: any }): Array<any>;
range(size: number): Array<number>;
}
declare var d3: _D3_;
Interface Files
app/flowdist/.flowconfig
[ignore]
.*node_modules/phantomjs.*
[include]
../node_modules/*
[libs]
../interfaces/
[options]
suppress_comment= \\(.\\|\n\\)*\\$FlowIssue
Babel
Flow
Source
Dist
es7.comprehensions
es7.classProperties
es7.doExpressions
es7.functionBind
es7.trailingFunctionCommas
es7.objectRestSpread
es7.decorators
es6.properties.computed
ES6/ES7 support:
https://github.com/facebook/flow/issues/560
Whitelisted Transformers:
(OSX)
(windows)
docker run --rm -it -v %cd%:/app motiz88/flow:0.17.0 bash
app/global/*
is currently passing flow!