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
show Flow output hide Flow output
$> 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
show Flow output hide Flow output
$> 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.
show Flow output hide Flow output
$> 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);
show Flow output hide Flow output
$> 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);
show Flow output hide Flow output
$> 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);
show Flow output hide Flow output
$> 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
show Flow output hide Flow output
$> 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
show Flow output hide Flow output
$> 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 });
show Flow output hide Flow output
$> 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);
show Flow output hide Flow output
$> 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.

← Prev Next →

You can edit this page on GitHub and send us a pull request!