- Basic syntax
- Flow Examples
- Advanced features
- Using with existing libraries (
flow-typed
andlibdefs
) - Tooling
- Try
// @flow
(1: number);
('foo': string);
(true: boolean);
(null: null);
(undefined: void);
(undefined: ?string);
(undefined: ?number);
([1, 2, 3]: Array<number>);
([1, 2, 3]: number[]);
([1, 'dsa', 3, null]: mixed[]);
({ foo: 'bar' }: { foo: string });
({ foo: 'bar', bar: 1 }: { foo: string });
// Annotating variables
let a: number = 1; // <- Allowed
let b: string = 1; // <- Not allowed
let c: number | string = 1; // <- Allowed
// Inferring types
let m = 1;
m = 'foo';
(m: string); // <- Allowed
(m: number); // <- Not allowed
(m: boolean); // <- Not allowed
// Literal types
(1: 1); // <- Allowed
(1: 'foo'); // <- Not Allowed
('foo': 'foo'); // <- Allowed
('foo': true); // <- Not Allowed
(true: true); // <- Allowed
({ foo: 'bar' }: { foo: 'bar' }); // <- Allowed
({ foo: 'bar' }: { foo: 'foo' }); // <- Not Allowed
Functions are structural types.
// @flow
function add(a: number, b: number): number {
return a + b;
}
add(2, 4); // <- Allowed
add('foo', 4); // <- Not Allowed
let double = (n: number): number => n * 2;
double(4); // <- Allowed
double('foo'); // <- Not Allowed
function withCallback(foo: Function) {
// Same as any
return foo();
}
withCallback(() => 5); // <- Allowed
withCallback(() => ({})); // <- Allowed
withCallback(() => 'str'); // <- Allowed
// With type annotation
function withCallback2(
foo: (a: number, b: number) => number
) {
return foo(5, 2);
}
withCallback2(add); // <- Allowed
withCallback2((a, b) => a + b); // <- Allowed
withCallback2(a => 'foo'); // <- Not Allowed
// Without named parameter
function withCallback3(foo: (number, number) => number) {
return foo(5, 2);
}
withCallback3(add); // <- Allowed
withCallback3((a, b) => a + b); // <- Allowed
withCallback3(a => 'foo'); // <- Not Allowed
// Own type
type Callback = (number, number) => number;
function withCallback4(foo: Callback) {
return foo(5, 2);
}
withCallback4(add); // <- Allowed
withCallback4((a, b) => a + b); // <- Allowed
withCallback4(a => 'foo'); // <- Not Allowed
Objects are structural types.
// @flow
let myObj: {
foo: Array<number>
};
myObj = { foo: [1, 2, 3] }; // <- Allowed
myObj = { foo: [1, 2, 3], bar: 'dsa' }; // <- Allowed
myObj = { bar: 'dsa' }; // <- Not Allowed
// Type Aliase
type MyObject = {
foo: Array<number>
};
let myObj2: MyObject;
myObj2 = { foo: [1, 2, 3] }; // <- Allowed
myObj2 = { foo: [1, 2, 3], bar: 'dsa' }; // <- Allowed
myObj2 = { bar: 'dsa' }; // <- Not Allowed
Interfaces are structurally typed, classes are nominal.
// @flow
interface JSONSerializable {
toJSON(): string
}
class Foo {
toJSON() {
return '';
}
}
class Bar {
toJSON() {
return '';
}
}
([new Foo(), new Bar()]: JSONSerializable[]); // <- Allowed
([new Foo(), new Bar()]: Foo[]); // <- Not Allowed
class Foo2 implements JSONSerializable {
toJSON() {
return JSON.stringify({ hello: 1 });
}
}
class Bar2 implements JSONSerializable {
toJSON() {
return JSON.stringify(42);
}
}
With objects:
// @flow
interface JSONSerializable {
toJSON(): string,
foo: number
}
const myObject = {
toJSON() {
return '';
},
foo: 1
};
(myObject: JSONSerializable); // <- Works
// exports.js
// @flow
export type MyObject = {
foo: Array<number>
};
export interface JSONSerializable {
toJSON(): string
}
export default function add(a: number, b: number): number {
return a + b;
}
// imports.js
// @flow
import type { MyObject, JSONSerializable } from './exports';
import typeof add from './exports';
const myAdd: add = (a, b) => a + b;
type Intersected = MyObject & JSONSerializable;
let myObject: Intersected = {
foo: [1, 2, 3],
toJSON() {
return '';
}
}; // <- Not allowed
({}: Intersected); // <- Not allowed
({ foo: [1] }: Intersected); // <- Not allowed
({ foo: [1], toJSON: 1 }: Intersected); // <- Not allowed
function prefix(data: ?string) {
if (!data) return 'No prefix';
return data + ' <- prefix!';
}
console.log(prefix('Foo'));
console.log(prefix('')); // No prefix
console.log(prefix()); // No prefix
Also not the same as optional argument:
function double(a?: number, b: ?number) {
return a + b;
}
double(1, 2); // <- Allowed
double(void 0, null); // <- Allowed
double(3, null); // <- Allowed
double(null, null); // <- Not allowed
Best to avoid null and if you have to do overloading, use defaults?
function double(a: number = 1, b: number = 2) {
return a + b;
}
double(1, 2);
double(void 0, void 0);
double(void 0, 3);
double(3);
let add = (a: 1, b: 2): number => a + b;
add(1, 2); // <- Not allowed
Can be used as enums:
type Action = 'DO_SOMETHING' | 'DO_SOMETHING_ELSE';
let dispatch = (action: Action) => action;
dispatch('DO_SOMETHING'); // <- Allowed
dispatch('ADD_NUMBERS'); // <- Not allowed
It's not exhaustive, but it makes sure you don't check for impossible code:
type Action = 'DO_SOMETHING' | 'DO_SOMETHING_ELSE';
function reducer(action: Action) {
switch (action) {
case 'Foo': // <- Not allowed
return 1;
case 'DO_SOMETHING':
return 1;
// <- Not exhaustive
}
}
reducer('DO_SOMETHING'); // <- Allowed
dispatch('Foo'); // <- Not allowed
But we can do a workaround/hack:
type Action = 'DO_SOMETHING' | 'DO_SOMETHING_ELSE';
function reducer(action: Action): number {
switch (action) {
case 'DO_SOMETHING':
return 1;
case 'DO_SOMETHING_ELSE':
return 2;
default:
return action; // <- Makes it an exhaustive list... :D (maybe not the best idea?)
}
}
reducer('DO_SOMETHING'); // <- Allowed
function identity<T>(a: T): T {
return a;
}
identity('a'); // <- Allowed
identity(3); // <- Allowed
let foo: string = identity(3); // <- Not Allowed
function add(one: mixed, two: mixed): mixed {
return one + two; // <- Not Allowed
}
But
function add(one: any, two: any): any {
return one + two; // <- Allowed
}
type Actions = 'FOO' | 'BAR';
type Action = { name: Actions };
function dispatch(action: Action) {
return action;
}
dispatch({ name: 'FOO', load: [1, 2, 3] });
Exact type:
type Actions = 'FOO' | 'BAR';
type Action = {| name: Actions |};
function dispatch(action: Action) {
return action;
}
dispatch({ name: 'FOO', load: [1, 2, 3] }); // <- Not allowed
var o: { [string]: number } = {};
o["foo"] = 0;
o["bar"] = 1;
var foo: number = o["foo"];
Can also intersect types:
type Actions = 'FOO' | 'BAR';
type Load = { load: Array<number> }
type Action = { name: Actions };
type ActionLoad = Action & Load;
function dispatch(action: ActionLoad) {
return action;
}
dispatch({ name: 'FOO', load: [1, 2, 3] }); // <- Allowed
dispatch({ load: [1, 2, 3] }); // <- Not Allowed
dispatch({ name: 'FOO' }); // <- Not Allowed
Doesn't work with exact object types (would be null type) or primitives:
type Actions = 'FOO' | 'BAR';
type Load = {| load: Array<number> |}
type Action = {| name: Actions |};
type ActionLoad = Action & Load;
function dispatch(action: ActionLoad) {
return action;
}
dispatch({ name: 'FOO', load: [1, 2, 3] }); // <- Not Allowed
dispatch({ load: [1, 2, 3] }); // <- Not Allowed
dispatch({ name: 'FOO' }); // <- Not Allowed
But can spread types:
type Actions = 'FOO' | 'BAR';
type Load = {| load: Array<number> |}
type Action = {| name: Actions |};
type ActionLoad = {| ...Action, ...Load |};
function dispatch(action: ActionLoad): string {
return action.name;
}
dispatch({ name: 'FOO', load: [1, 2, 3] }); // <- Allowed
dispatch({ name: 'FOO', load: [1, 2, 3], extra: 'dsa' }); // <- Not Allowed
type Actions = 'FOO' | 'BAR';
type Load = {| load: Array<number> |};
type Action = {| name: Actions |};
type ActionLoad = {| ...Action, ...Load |};
let action: ActionLoad = { name: 'FOO', load: [1, 2, 3] };
let action2: typeof action = { foo: 1 }; // <- Not allowed
function clone(a: Array<mixed>) {
return ((a.slice(): any): typeof a);
}
let r = clone([1, 2, 3]);
(r[0]: 1); // <- Doesn't work
But:
// @flow
function clone(a) {
(a: Array<mixed>);
return ((a.slice(): any): typeof a);
}
let r = clone([1, 2, 3]);
(r[0]: 1); // <- Works
Better solution:
// @flow
function clone<T: Array<mixed>>(a: T): T {
return ((a.slice(): any): typeof a);
}
let r = clone([1, 2, 3]);
(r[0]: 1); //<- Allowed
(r[1]: 3); //<- Not Allowed
// @flow
import React from 'react';
class Tooltip extends React.Component {
props: {
text: string,
onMouseOver: ({x: number, y: number}) => void
};
}
const someProps: $PropertyType<Tooltip, 'props'> = {
text: 'foo',
onMouseOver: (data: {x: number, y: number}) => undefined
};
const otherProps: $PropertyType<Tooltip, 'props'> = {
text: 'foo'
// Missing the `onMouseOver` definition
};
Use flow-typed
or define library definitions manually in /flow-typed
(from root) for Flow to find definitions for third party code.
TODO