Notes on Flow
A copy paste of selections from the documentation.
Flow is a static type checker for your JavaScript code.
You can write type annotations on your code to allow Flow to check your code for errors.
For example:
// @flow
function square(n: number): number {
return n * n;
}
square("2"); // Error!
Setup
To allow Flow to check this code for type errors, configure your project to use Flow:
-
Install Flow and a compiler to strip away type annotations
Babel or flow-remove-types can be installed to remove the type annotations from your code.
When using babel, install
babel-cli
andbabel-preset-flow
npm install babel-cli babel-preset-flow
Add flow to your babel presets array in
.babelrc
{ "presets": ["flow"] }
-
Add flow
npm install --save-dev flow-bin
Add a flow script to your
package.json
{ "name": "my-flow-project", "version": "1.0.0", "devDependencies": { "flow-bin": "^0.41.0" }, "scripts": { "flow": "flow" } }
-
Configure the project to use Flow
npm run flow init
This creates a
.flowconfig
file which allows you to configure and customize Flow such as the path to the code. -
Run flow to check for type errors
npm run flow
This is equivalent to
flow status
npm run flow status
Which runs flow as a background process.
Only one instance of the background process regardless of how many times
flow status
is calledTo stop the background process
npm run flow stop
Flow will check source code files for error if the file begins with flow flag:
// @flow /* @flow */
It will skip files without the flag unless
flow check --all
is called.
Writing Flow
You can now begin to write JavaScript code with type annotations.
// @flow
function foo(x: ?number): string {
if (x) {
return x;
}
return "default string";
}
For the code above running flow will yield the following:
test.js:5
5: return x;
^ number. This type is incompatible with the expected return type of
3: function foo(x: ?number): string {
^^^^^^ string
Primitive Types
Primitive types can be literal values true "hello" 3.14 null undefined
or constructed wrapper objects new Boolean(false) new String("world")
.
Types for literal values are lowercase and wrapper objects are capitalized.
Boolean values need to be explicitly converted to boolean using Boolean(x) or !!x
.
Maybe Types
Maybe types are for places where a value is optional and you can create them by adding a question mark in front of the type such as ?string
or ?number
.
Optional Object Properties
Object types can have optional properties where a question mark ?
comes after the property name.
{ propertyName?: string }
In addition to their set value type, these optional properties can either be void or omitted altogether. However, they cannot be null
.
// @flow
function acceptsObject(value: { foo?: string }) {
// ...
}
acceptsObject({ foo: "bar" }); // Works!
acceptsObject({ foo: undefined }); // Works!
acceptsObject({ foo: null }); // Error!
acceptsObject({}); // Works!
Optional function parameters
Functions can have optional parameters where a question mark ?
comes after the parameter name.
function method(param?: string) { /* ... */ }
In addition to their set type, these optional parameters can either be void or omitted altogether. However, they cannot be null
.
// @flow
function acceptsOptionalString(value?: string) {
// ...
}
acceptsOptionalString("bar"); // Works!
acceptsOptionalString(undefined); // Works!
acceptsOptionalString(null); // Error!
acceptsOptionalString(); // Works!
Function parameters with defaults
Function parameters can also have defaults. This is a feature of ECMAScript 2015.
function method(value: string = "default") { /* ... */ }
In addition to their set type, default parameters can also be void or omitted altogether. However, they cannot be null
.
// @flow
function acceptsOptionalString(value: string = "foo") {
// ...
}
acceptsOptionalString("bar"); // Works!
acceptsOptionalString(undefined); // Works!
acceptsOptionalString(null); // Error!
acceptsOptionalString(); // Works!
Symbols
Symbols are not currently supported by Flow. You can see these two issues for more information:
Literal Types
Using literal values as types
Flow has primitive types for literal values, but can also use literal values as types.
For example, instead of accepting number type, we could accept only the literal value 2.
// @flow function acceptsTwo(value: 2) { // ... } acceptsTwo(2); // Works! // $ExpectError acceptsTwo(3); // Error! // $ExpectError acceptsTwo("2"); // Error!
Using these with union types is powerful:
// @flow function getColor(name: "success" | "warning" | "danger") { switch (name) { case "success" : return "green"; case "warning" : return "yellow"; case "danger" : return "red"; } } getColor("success"); // Works! getColor("danger"); // Works! // $ExpectError getColor("error"); // Error!
Mixed Types
A single type:
Here the input value can only be a number.
function square(n: number): number {
return n * n;
}
A group of different possible types:
function stringifyBasicValue(value: string | number) {
return '' + value;
}
A type based on another type:
Here the return type will be the same as the type of whatever value is passed into the function.
function identity<T>(value: T): T {
return value;
}
These three are the most common categories of types. They will make up the majority of the types you’ll be writing.
However, there is also a fourth category.
An arbitrary type that could be anything:
Here the passed in value is an unknown type, it could be any type and the function would still work.
function getTypeOf(value: mixed): string {
return typeof value;
}
When you try to use a value of a mixed
type you must first figure out what the actual type is or you’ll end up with an error.
// @flow
function stringify(value: mixed) {
// $ExpectError
return "" + value; // Error!
}
stringify("foo");
Instead you must ensure the value is a certain type by refining it.
// @flow
function stringify(value: mixed) {
if (typeof value === 'string') {
return "" + value; // Works!
} else {
return "";
}
}
stringify("foo");
Because of the typeof value === 'string'
check, Flow knows the value
can only be a string
inside of the if
statement. This is known as a refinement.