1. types
1.1 Primitive Types
Types for literal values are lowercase.
// @flow
function method(x: number, y: string, z: boolean) {
// ...
}
method(3.14, "hello", true);
Types for the wrapper objects are capitalized (the same as their constructor).
// @flow
function method(x: Number, y: String, z: Boolean) {
// ...
}
method(new Number(42), new String("world"), new Boolean(false));
string
// @flow
function acceptString(value: string) {
console.log(value)
}
acceptString('str' + 'foo') // works
acceptString('str' + 42) // works
acceptString('str' + {}) // error
acceptString('str' + []) // error
acceptString('str' + {}.toString()) // works
acceptString('str' + String([])) // works
null&undefined
Flow treats these as separate types: null and void (for undefined).
// @flow
function acceptsUndefined(value: void) {
/* ... */
}
acceptsUndefined(undefined) // works
Function parameters with defaults
// @flow
function acceptsOptionalString(value: string = "foo") {
// ...
}
acceptsOptionalString("bar"); // Works!
acceptsOptionalString(undefined); // Works!
acceptsOptionalString(null); // Error!
acceptsOptionalString(); // Works!
1.2 Literal Types
// @flow
// string
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!
// number
function acceptsTwo(value: 2) {
// ...
}
acceptsTwo(2); // Works!
// $ExpectError
acceptsTwo(3); // Error!
// $ExpectError
acceptsTwo("2"); // Error!
1.3 Mixed Types
function stringifyBasicValue(value: string | number | boolean) {
return '' + 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;
}
stringify("foo");
// Error! This type cannot be used in an addition
// because it is unkonwn whether it behaves like a number or a string
// @flow
function stringify(value: mixed) {
if (typeof value === 'string') {
return "" + value; // Works!
} else {
return "";
}
}
stringify("foo"); // works
1.4 Any Types
// @flow
function fn(obj: any) /* (:number) */ {
let foo: number = obj.foo;
let bar /* (:number) */ = foo * 2;
return bar;
}
let bar /* (:number) */ = fn({ foo: 2 });
let baz /* (:string) */ = "baz:" + bar;
1.5 Maybe Types
// @flow
function acceptsMaybeNumber(value: ?number) {
if (value !== null && value !== undefined) {
return value * 2;
}
}
function acceptsMaybeNumber(value: ?number) {
if (value != null) {
return value * 2;
}
}
function acceptsMaybeNumber(value: ?number) {
if (typeof value === 'number') {
return value * 2;
}
}
1.6 Function Types
function method(str, bool, ...nums) {
// ...
}
function method(str: string, bool?: boolean, ...nums: Array<number>): void {
// ...
}
// Arrow Functions
let method = (str: string, bool?: boolean, ...nums: Array<number>): void => {
// ...
}
Function Types
(str: string, bool?: boolean, ...nums: Array<number>) => void
// You may also optionally leave out the parameter names
(string, boolean | void, Array<number>) => void
// You might use these functions types for something like a callback
function method(callback: (error: Error | null, value: string | null) => void) {
// ...
}
Rest Parameters
function method(...args: Array<number>) {
// ...
}
Function Returns
function method(): number {
// ...
}
Return types ensure that every branch of your function returns the same type. This prevents you from accidentally not returning a value under certain conditions.
// @flow
// $ExpectError
function method(): boolean {
if (Math.random() > 0.5) {
return true;
}
}
1.7 Object Types
Accessing a property that doesn’t exist evaluates to undefined.If an Object sometimes donesn’t have a property,use optional object type.
// @ flow
var obj: { foo?: boolean } = {}
obj.foo = true // works
Sealed objects
// @flow
var obj = {
foo: 1,
bar: true,
baz: 'three'
};
var foo: number = obj.foo; // Works!
var bar: boolean = obj.bar; // Works!
// $ExpectError
var baz: null = obj.baz; // Error!
var bat: string = obj.bat; // Error!
// @flow
var obj = {
foo: 1
};
// $ExpectError
obj.bar = true; // Error!
// $ExpectError
obj.baz = 'three'; // Error!
Unsealed objects
// @flow
var obj = {};
obj.foo = 1; // Works!
obj.bar = true; // Works!
obj.baz = 'three'; // Works!
var obj = {};
obj.foo = 42;
var num: number = obj.foo;
Exact object types
function foo(onj:{| foo: string, bar: number |}) {
// ...
}
foo({foo: 'str', bar: 1}) // works
foo({foo: 'str', bar: 1, baz: 2}) // error
1.8 Type Aliases
// @flow
type MyObject = {
foo: number,
bar: boolean,
baz: string,
};
These type aliases can be used anywhere a type can be used
var val: MyObject = { / *... * / }
function method(params: MyObject) = { / *... * / }
class Foo { constructor(val: MyObject) { / *... * / } }
1.9 Interface Types
// @flow
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
// $ExpectError
const foo: Foo = new Bar(); // Error!
Instead, you can use interface in order to declare the structure of the class that you are expecting
interface Serializable {
serizalize(): string
}
class Foo {
serialize() { return '[Foo]'; }
}
class Bar {
serialize() { return '[Bar]'; }
}
const foo: Serializable = new Foo(); // Works!
const bar: Serializable = new Bar(); // Works!
implements
You can also use implements to tell Flow that you want the class to match an interface. This prevents you from making incompatible changes when editing the class.
// @flow
interface Serializable {
serialize(): string;
}
class Foo implements Serializable {
serialize() { return '[Foo]'; } // Works!
}
class Bar implements Serializable {
// $ExpectError
serialize() { return 42; } // Error!
}
Interface property variance
// @flow
// + read-only
interface Invariant { property: number | string; }
interface Covariant { +readOnly: number | string; }
function method1(value: Invariant) {
value.property; // Works!
value.property = 3.14; // Works!
}
function method2(value: Covariant) {
value.readOnly; // Works!
// $ExpectError
value.readOnly = 3.14; // Error!
}
// - write-only
interface Invariant { property: number; }
interface Contravariant { -writeOnly: number; }
var numberOrString = Math.random() > 0.5 ? 42 : 'forty-two';
// $ExpectError
var value1: Invariant = { property: numberOrString }; // Error!
var value2: Contravariant = { writeOnly: numberOrString }; // Works!
1.10 Union Types
type Numbers = 1 | 2;
type Colors = 'red' | 'blue'
type Fish = Numbers | Colors;
Disjoint Unions
// @flow
// Trying to combine these two separate types into a single one will only cause us trouble
type Response = {
success: boolean,
value?: boolean,
error?: string
};
function handleResponse(response: Response) {
if (response.success) {
// $ExpectError
var value: boolean = response.value; // Error!
} else {
// $ExpectError
var error: string = response.error; // Error!
}
}
Instead, if we create a union type of both object types, Flow will be able to know which object we’re using based on the success property.
// create a union type of both object types
type Success = { success: true, value: boolean };
type Failed = { success: false, error: string };
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value; // Works!
} else {
var error: string = response.error; // Works!
}
}
Disjoint unions with exact types
You cannot distinguish two different objects by different properties.
// @flow
type Success = { success: true, value: boolean };
type Failed = { error: true, message: string };
function handleResponse(response: Success | Failed) {
if (response.success) {
// $ExpectError
var value: boolean = response.value; // Error! property 'value' not found in object type
}
}
Flow checks not beyond judgement, so it’s result is no connection with if.
However, to get around this you could use exact object types.
// @flow
type Success = {| success: true, value: boolean |};
type Failed = {| error: true, message: string |};
type Response = Success | Failed;
function handleResponse(response: Response) {
if (response.success) {
var value: boolean = response.value;
} else {
var message: string = response.message;
}
}
With exact object types, we cannot have additional properties, so the objects conflict with one another and we are able to distinguish which is which.
1.11 Typeof Types
// @flow
let num1 = 42;
let num2: typeof num1 = 3.14; // Works!
// $ExpectError
let num3: typeof num1 = 'world'; // Error!
let bool1 = true;
let bool2: typeof bool1 = false; // Works!
// $ExpectError
let bool3: typeof bool1 = 42; // Error!
let str1 = 'hello';
let str2: typeof str1 = 'world'; // Works!
// $ExpectError
let str3: typeof str1 = false; // Error!
let obj1 = { foo: 1, bar: true, baz: 'three' };
let obj2: typeof obj1 = { foo: 42, bar: false, baz: 'hello' };
let arr1 = [1, 2, 3];
let arr2: typeof arr1 = [3, 2, 1];