Five simple examples

Edit on GitHub

Throughout each of the following examples, we will check our code using the flow CLI. You can install this CLI using npm by running npm install --global flow-bin.

Note also that the first time you run flow on a given example (or project), it will normally take a few seconds, but subsequent runs are much quicker. This is because running flow will start a process in the background which monitors the project you are working within and incrementally recalculate type errors with each change you save to disk. If you wish to stop the background Flow process, you can do so with the flow stop command.

If you wish to just typecheck a project without a persistent process (and you don't mind waiting a few extra seconds each time) you can use flow check. This will ensure that Flow shuts down immediately after checking your project.

1. Hello Flow!

Inside the Flow GitHub repo, you'll find an examples directory. This directory contains the examples for this tutorial. To get a feel for Flow, let's look at the first one:

  $> cd flow/examples/01_HelloWorld
  $> flow

You should see an error that looks something like this:

  hello.js:7
    7: foo("Hello, world!");
       ^^^^^^^^^^^^^^^^^^^^ function call
    4:   return x * 10;
                ^ string. This type is incompatible with
    4:   return x * 10;
                ^^^^^^ number

Looking at the hello.js example file, it's easy to see why:

1
2
3
4
5
6
7
// @flow

function foo(x) {
  return x * 10;
}

foo('Hello, world!');

We're calling a function with a string, when that function clearly expects a number. Flow detects this problem and gives an error. One fix for this example would be to call foo with a number instead:

1
2
3
4
5
6
7
8
// @flow

function foo(x) {
  return x * 10;
}

// This is fine, because we're passing a number now
foo(10);

Throughout this tutorial, you will find solutions for each example in the example's answer directory.

You may have noticed this header line in the example file:

// @flow

This is important: it tells Flow that this file should be typechecked. Flow will ignore any files that don't have this header. This allows you to convert and/or typecheck a JS project one file at a time.

2. Adding type annotations

Flow can infer the type of most things within a file, so you don't always have to annotate every function and variable to get typechecking to work. However, even if Flow can infer a type, you can still add annotations to be explicit. The only time that Flow strictly requires an annotation is when a variable/function/class is exported from a module (defined in one file and used in another).

The second example (02_TypeAnnotations) shows usage of some basic type annotations in Flow:

1
2
3
4
5
6
7
// @flow

function foo(x: string, y: number): string {
  return x.length * y;
}

foo('Hello', 42);

Here we have annotated the foo function to say that it's two parameters are of type string and number and that it returns a string.

With these annotations in place, if we run flow check we'll see an error:

type_annotations.js:4
  4:   return x.length * y;
              ^^^^^^^^^^^^ number. This type is incompatible with
  3: function foo(x: string, y: number): string {
                                         ^^^^^^ string

In this case it is the return type for foo that is wrong. Even though we have annotated it as returning a string, the actual type that it returns (a number) does not match! Flow flags this issue and you can fix it by correcting the return type:

1
2
3
4
5
6
7
8
// @flow

// Changing the return type to number fixes the error
function foo(x: string, y: number): number {
  return x.length * y;
}

foo('Hello', 42);

3. Nullable types

Flow handles null differently than most type systems. When a type system does not track usage of null carefully, it becomes possible for your program to misleadingly typecheck as correct but still crash when null is accessed in an unsafe way. In Flow, accessing null in an unsafe way will incur an error as shown by our third example (03_Null):

1
2
3
4
5
6
7
// @flow

function length(x) {
  return x.length;
}

var total = length('Hello') + length(null);

This program would fail at runtime, with a TypeError when it tries to read the property length on null. Running flow will detect this bug:

  nulls.js:7
    7: var total = length("Hello") + length(null);
                                     ^^^^^^^^^^^^ function call
    4:   return x.length;
                  ^^^^^^ property `length`. Property cannot be accessed on possibly null value
    4:   return x.length;
                ^ null

The file in the answer directory fixes this bug just like you might if you had discovered it at runtime. This fix, in turn, makes the type error go away:

1
2
3
4
5
6
7
8
9
10
11
// @flow

function length(x) {
  if (x !== null) {
    return x.length;
  } else {
    return 0;
  }
}

var total = length('Hello') + length(null);

Because we've checked that x is not null, Flow knows that this code is now safe and doesn't emit a type error.

4. Arrays

Flow is of course not limited to simple types like numbers and strings. Our next example, 04_Arrays, illustrates support for annotating functions on arrays:

1
2
3
4
5
6
7
8
9
10
11
// @flow

function total(numbers: Array<number>) {
  var result = 0;
  for (var i = 0; i < numbers.length; i++) {
    result += numbers[i];
  }
  return result;
}

total([1, 2, 3, 'Hello']);

Flow will flag the string 'Hello' here since the total() function accepts only an array of numbers:

  arrays.js:11
   11: total([1, 2, 3, "Hello"]);
       ^^^^^^^^^^^^^^^^^^^^^^^^^ function call
   11: total([1, 2, 3, "Hello"]);
                       ^^^^^^^ string. This type is incompatible with
    3: function total(numbers: Array<number>) {
                                     ^^^^^^ number

If we replace "Hello" with a number, the code will pass Flow's checks:

1
2
3
4
5
6
7
8
9
10
11
// @flow

function total(numbers: Array<number>) {
  var result = 0;
  for (var i = 0; i < numbers.length; i++) {
    result += numbers[i];
  }
  return result;
}

total([1, 2, 3, 4]);

5. Dynamic code

In our final example, 05_DynamicCode, we haven't annotated the function, but we are passing in two different types of arguments:

1
2
3
4
5
6
7
// @flow

function foo(x) {
  return x.length;
}

var res = foo('Hello') + foo(42);

In this case, Flow detects that the second time the function is called (with a number), the length property will fail:

  dynamic.js:4
    4:   return x.length;
                ^^^^^^^^ property `length`
    4:   return x.length;
                  ^^^^^^ property `length`. Property not found in
    4:   return x.length;
                ^ Number

One fix is to simply detect what the type is within the function:

1
2
3
4
5
6
7
8
9
10
11
// @flow

function foo(x) {
  if (typeof x === 'string') {
    return x.length;
  } else {
    return x;
  }
}

var res = foo('Hello') + foo(42);

Flow is smart enough to detect that this conditional check is sufficient to avoid any potential failures at run time, and will give you a clean bill of health.

Next Steps

These simple examples just scratch the surface. You're now ready to start a new project with Flow and use the offline transform tool to compile type annotations before publishing. Or you could incrementally try Flow using flow on some existing code. You may also want to check out our much bigger React example to see Flow in more representative use cases.