Built-in Types
Flow includes many built-in types, which can be used to describe values in JavaScript.
There are types for primitive values, like number
and string
. Types like
any
and mixed
describe more flexible constraints on values, whereas
literal types describe specifically a single value.
Flow comes out of the box with support for the JavaScript standard library, Browser APIs like the DOM, and the Node.js standard library.
Note about typecast syntax#
The following examples make extensive use of typecasts in order to demonstrate type compatibility.
In Flow, typecasts are sound, meaning that the statement (e:T)
for some
expression e
and type T
is a type error unless the inferred type of e
is
a valid subtype of T
.
In other words, with respect to validity a typecast behaves just like a type
annotation on a variable or parameter. If var x:T = e
would be valid, then
(e:T)
would be as well.
1 2 3 4 5 6 7 8 9 |
(1 + 1: string); // Error: Numbers are not strings ("Hello, World": string); // OK: Strings are strings class A {} class B extends A {} let a = new A(), b = new B(); (b: A); // OK: B is a subclass of A (a: B); // Error: A is not a subclass of B |
$> flow
1: (1 + 1: string); // Error: Numbers are not strings
^^^^^ number. This type is incompatible with
1: (1 + 1: string); // Error: Numbers are not strings
^^^^^^ string
9: (a: B); // Error: A is not a subclass of B
^ A. This type is incompatible with
9: (a: B); // Error: A is not a subclass of B
^ B
boolean#
This type describes a boolean value in JavaScript. The possible values of this
type are true
and false
.
1 2 3 |
(true: boolean); (false: boolean); ("foo": boolean); // strings are not booleans |
$> flow
3: ("foo": boolean); // strings are not booleans
^^^^^ string. This type is incompatible with
3: ("foo": boolean); // strings are not booleans
^^^^^^^ boolean
JavaScript specifies many implicit conversions, which provide boolean
semantics to values of other types. Flow understands this and allows any
expression to be used as a conditional in an if
statement or as an operand
to &&
. However, if you need to cast an object specifically to the boolean
type, you can use the built-in Boolean
function
1 2 3 |
function takes_boolean(x: boolean): void {} takes_boolean(0); // Implicit casting is an error. takes_boolean(Boolean(0)); // Adding an explicit cast type checks. |
$> flow
2: takes_boolean(0); // Implicit casting is an error.
^ number. This type is incompatible with the expected param type of
1: function takes_boolean(x: boolean): void {}
^^^^^^^ boolean
Note that boolean
and Boolean
are separate types. The former is the type
of primitive booleans which appear in programs as literals true
and false
,
or as the result of expressions like a === b
. The latter is the type of
Boolean wrapper objects, which are rarely used.
1 2 |
(true: Boolean); (new Boolean(false): Boolean); |
$> flow
1: (true: Boolean);
^^^^ boolean. This type is incompatible with
1: (true: Boolean);
^^^^^^^ Boolean
number#
JavaScript has a single number type, which is IEEE 754 floating point numbers.
The number
type describes these values, which includes Infinity
and NaN
.
(3.14: number);
(42: number);
(NaN: number);
(parseFloat("not a number"): number); // hint: NaN
Note that number
and Number
are separate types. The former is the type of
primitive numbers which appear in programs as literals, like 3.14
and 42
,
or as the result of expressions like parseFloat(input.value)
. The latter is
the type of Number wrapper objects, which are rarely used.
1 2 |
(0: Number); (new Number(0): Number); |
$> flow
1: (0: Number);
^ number. This type is incompatible with
1: (0: Number);
^^^^^^ Number
string#
("foo": string);
("bar": string);
Generally, implicit type casting is an error with Flow. However, it is a
fairly common JavaScript idiom to produce a string by combining a string
and
a number
with the binary operator +
, so Flow accepts it.
((100 + "%") : string);
Note that string
and String
are separate types. The former is the type of
primitive strings which appear in programs as literals, like "foo"
and
"bar"
, or as the result of expressions like "" + 42
. The latter is the
type of String wrapper objects, which are rarely used.
1 2 |
("foo": String); (new String("foo"): String); |
$> flow
1: ("foo": String);
^^^^^ string. This type is incompatible with
1: ("foo": String);
^^^^^^ String
null and void#
JavaScript has both null
and undefined
, which Flow is careful to treat
separately. null
(the value) has the type null
. undefined
has the type
void
.
1 2 3 4 5 |
(null: null); // yup (null: void); // nope (undefined: void); // yup (undefined: null); // nope |
$> flow
2: (null: void); // nope
^^^^ null. This type is incompatible with
2: (null: void); // nope
^^^^ undefined
5: (undefined: null); // nope
^^^^^^^^^ undefined. This type is incompatible with
5: (undefined: null); // nope
^^^^ null
Optional object properties and optional function parameters have the type
T|void
, for some type T
.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function optional_fun(foo?: string) { (foo: string|void); } optional_fun("foo"); optional_fun(undefined); optional_fun(); optional_fun(null); // null is not a string, nor void type optional_obj = { foo?: string } ({foo: "foo"}: optional_obj); ({foo: undefined}: optional_obj); ({}: optional_obj); ({foo: null}: optional_obj); // null is not a string, nor void |
$> flow
7: optional_fun(null); // null is not a string, nor void
^^^^ null. This type is incompatible with the expected param type of
1: function optional_fun(foo?: string) {
^^^^^^ string
13: ({foo: null}: optional_obj); // null is not a string, nor void
^^^^^^^^^^^ object literal. This type is incompatible with
13: ({foo: null}: optional_obj); // null is not a string, nor void
^^^^^^^^^^^^ object type
Function parameters that have a default are optional as well, but only for
callers. Within the function body, the binding has a non-void
type.
function default_fun(foo: string = "default foo") {
(foo: string);
}
default_fun("foo");
default_fun(undefined);
default_fun();
Maybe types have the type T|void|null
for some type T
.
function maybe_fun(foo: ?string) {
(foo: string|void|null);
}
maybe_fun("foo");
maybe_fun(undefined);
maybe_fun();
maybe_fun(null);
any#
any
is simultaneously a supertype of all types and a subtype of all types.
Intuitively, an any
value can take the place of “any” other value, and Flow
will understand that to be well-typed.
function takes_any(x: any): void {}
takes_any(0);
takes_any("");
takes_any({ foo: "bar" });
declare var unsafe: any;
(unsafe: number);
(unsafe: string);
(unsafe: { foo: string });
In addition to compatibility between types, it’s useful to think of any
in
terms of operations that can be performed on values. Accessing any property on
an any
will yield an any
. It is possible to call an any
as a function,
taking any number arguments of any type, which will also return any
.
unsafe.foo.bar.baz;
(unsafe("foo"): string);
(unsafe("bar"): number);
You can think of any
as a kind of “backdoor” in the type system. Use of
any
is inherently unsafe and should be avoided whenever possible. However,
it can also be incredibly convenient.
For example, when adding types to existing code, using any
can help make the
gradual transition from untyped to typed code. Similarly, modeling third-party
APIs with any
can ease integration.
Lastly, due to the highly dynamic nature of JavaScript, there are some idioms
which Flow does not yet understand. Principled use of any
makes it possible
to wrap untyped code in types.
mixed#
Like any
, mixed
is a supertype of all types. Unlike any
, however,
mixed
is not a subtype of all types. This means mixed
is like a safe
but somewhat annoying version of any
. It should be preferred over any
whenever possible.
1 2 3 4 5 6 7 8 9 |
function takes_mixed(x: mixed): void {} takes_mixed(0); takes_mixed(""); takes_mixed({ foo: "bar" }); function returns_mixed(): mixed {} (returns_mixed(): number); (returns_mixed(): string); (returns_mixed(): { foo: string }); |
$> flow
7: (returns_mixed(): number);
^^^^^^^^^^^^^^^ mixed. This type is incompatible with
7: (returns_mixed(): number);
^^^^^^ number
8: (returns_mixed(): string);
^^^^^^^^^^^^^^^ mixed. This type is incompatible with
8: (returns_mixed(): string);
^^^^^^ string
9: (returns_mixed(): { foo: string });
^^^^^^^^^^^^^^^ mixed. This type is incompatible with
9: (returns_mixed(): { foo: string });
^^^^^^^^^^^^^^^ object type
It’s still possible to use a value with a mixed
, but you must first
refine the value.
For example, let’s construct a type of values which can be expressed directly as JSON. We can express this type quite naturally.
type JSON = | string | number | boolean | null | JSONObject | JSONArray;
type JSONObject = { [key:string]: JSON };
type JSONArray = Array<JSON>;
Now let’s write a function that verifies that a given value is a JSON value.
If we annotated the parameter as any
, we could just return the parameter and
Flow would accept that without error because any
is a subtype of all types,
including JSON
.
If we use mixed
, however, we can still pass any value into our function, as
mixed
is a supertype of all types. But in order to satisfy the JSON
return
type, Flow requires us to implement the necessary runtime type checks.
function typedJSON(x: mixed): JSON {
if (typeof x === "object" && x !== null) {
let o: JSONObject = {};
for (let k of Object.keys(x)) {
o[k] = typedJSON(x[k]);
}
return o;
}
if (Array.isArray(x)) {
return x.map(typedJSON);
}
if (x === null ||
typeof x === "string" ||
typeof x === "number" ||
typeof x === "boolean") {
return x;
}
throw new Error("Invalid JSON");
}
literal types#
While type boolean
, number
, and string
types admit true
and false
,
any number, and any string, respectively, it can also be useful to specify a
type that admits a single value. This feature turns out to be surprisingly
versatile: literal types can be used to build enums
and other disjoint unions, as well as express some common forms of method
overloading for which the types boolean
, number
, and string
are not
adequate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
("foo": "foo"); ("bar": "foo"); // `"bar"` is not exactly `"foo"` ("fo"+"o": "foo"); // even simple expressions lose literal information (1: 1); (2: 1); // `2` is not exactly `1` (1+1: 2); // even simple expressions lose literal information (true: true); (true: false); // `true` is not exactly `false` // boolean expressions *do* preserve literal information (!true: false); (true && false: false); (true || false: true); |
$> flow
2: ("bar": "foo"); // `"bar"` is not exactly `"foo"`
^^^^^ string. Expected string literal `foo`, got `bar` instead
2: ("bar": "foo"); // `"bar"` is not exactly `"foo"`
^^^^^ string literal `foo`
3: ("fo"+"o": "foo"); // even simple expressions lose literal information
^^^^^^^^ string. Expected string literal `foo`
3: ("fo"+"o": "foo"); // even simple expressions lose literal information
^^^^^ string literal `foo`
6: (2: 1); // `2` is not exactly `1`
^ number. Expected number literal `1`, got `2` instead
6: (2: 1); // `2` is not exactly `1`
^ number literal `1`
7: (1+1: 2); // even simple expressions lose literal information
^^^ number. Expected number literal `2`
7: (1+1: 2); // even simple expressions lose literal information
^ number literal `2`
10: (true: false); // `true` is not exactly `false`
^^^^ boolean. Expected boolean literal `false`, got `true` instead
10: (true: false); // `true` is not exactly `false`
^^^^^ boolean literal `false`
Let’s have a little fun with literal types. The following program shows how literals can be used to statically index into objects. It also shows how tuples of unions can be used to represent enums.
type Suit =
| "Diamonds"
| "Clubs"
| "Hearts"
| "Spades";
type Rank =
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
| "Jack"
| "Queen"
| "King"
| "Ace";
type Card = {
suit: Suit,
rank: Rank,
}
declare var cards: Card[];
cards.sort((a, b) => cardComparator(a, b));
cards.sort((a, b) => cardComparator(a, b, true)); // Aces high
function suitOrder(suit) {
return {
Diamonds: 0,
Clubs: 1,
Hearts: 3,
Spades: 4,
}[suit];
}
function rankOrder(rank, aceHigh = false) {
if (typeof rank === "string") {
return {
Jack: 11,
Queen: 12,
King: 13,
Ace: aceHigh ? 14 : 1,
}[rank];
} else {
return rank;
}
}
function cardComparator(a, b, aceHigh?) {
return (rankOrder(a.rank, aceHigh) - rankOrder(b.rank, aceHigh))
|| (suitOrder(a.suit) - suitOrder(b.suit));
}
Note that Flow is able to infer the parameter and return types of suitOrder
,
rankOrder
, and cardComparator
correctly. Try copying that into your text
editor and introducing type errors to see what Flow can catch!
JavaScript standard library#
Type declarations for the JavaScript standard library are included with Flow.
Flow has broad support for the standard library, including support for Iterables, Iterators, and Generators.
Browser APIs#
Type declarations for the Document Object Model (DOM), Browser Object Model (BOM), and CSS Object Model (CSSOM) are all included with Flow.
Node.js#
Type declarations for the Node.js standard library are included with Flow.
You can edit this page on GitHub and send us a pull request!