Type Annotations

JavaScript is inherently a dynamically-typed language. As such, explicitly typing your code is not part of the JavaScript lexicon. This is normal JavaScript code:

1
2
3
4
5
function add(num1, num2) {
  return num1 + num2;
}
var x = add(3, '0');
console.log(x);

What is the value of x? 3? "30"? undefined? The answer is "30", and, in most cases, this probably not the behavior you would prefer.

Flow helps mitigate these sort of subtle bugs by trying to keep your code sane through static analysis and type annotations.

Type Annotations#

Type annotations are generally prefixed by :. And they can be placed on function parameters, function return types and variable declarations. e.g.,

1
2
3
4
5
6
function foo(a: string, b: number): void { ... }
var x: boolean = someBool;
class Bar {
  y: string;
  someMethod(a: number): string { ... }
}

Simple Example#

We can easily take this code and make it Flow aware by adding a simple annotation @flow at the top in a comment block:

1
2
3
4
5
6
/* @flow */
function add(num1, num2) {
  return num1 + num2;
}
var x = add(3, '0');
console.log(x);

However, Flow will find no errors with the above code. That’s because the + operator is perfectly acceptable on numbers and strings, and we didn’t specify that the parameters to add must be numbers.

1
2
3
4
5
6
/* @flow */
function add(num1: number, num2: number): number {
  return num1 + num2;
}
var x: number = add(3, '0');
console.log(x);

Running the type checker against the above code will yield type errors since we have explicitly typed all parameters and variables.

file.js:5
  5: var x: number = add(3, '0');
                     ^^^^^^^^^^^ function call
  5: var x: number = add(3, '0');
                            ^^^ string. This type is incompatible with
  2: function add(num1: number, num2: number): number {
                                      ^^^^^^ number

Found 1 error

Type Annotation Requirements#

Type annotations are not always strictly necessary to use Flow. As shown above, all that is strictly required to make your JavaScript file Flow aware is the @flow annotation. And this annotation by itself can be enough for Flow to deduce all that is necessary to type check your code.

1
2
3
4
5
6
/* @flow */
function multPI(num1, num2) {
  return Math.PI * num1 * num2;
}
var x = multPI(3, '0');
console.log(x);

Since the multiplication operator makes no real sense on a string, Flow is smart enough to deduce a problem here without explicit type annotations.

file.js:5
  5: var x = multPI(3, '0');
             ^^^^^^^^^^^^^^ function call
  3:   return Math.PI * num1 * num2;
                               ^^^^ string. This type is incompatible with
  3:   return Math.PI * num1 * num2;
              ^^^^^^^^^^^^^^^^^^^^^ number

Found 1 error

Module Boundaries#

Flow requires annotations at the boundaries of modules. This allows Flow to analyze modules in isolation which improves the performance of checking types across module boundaries. We’ve found that this helps to improve the self-documenting nature of module interfaces as well.

1
2
3
4
5
6
7
8
9
/**
 * Size.js
 * @flow
 */
function size(input: string): number {
  return input.length;
}

module.exports = size;
1
2
3
4
5
6
/**
 * UseSize.js
 * @flow
 */
var size = require('./Size');
var result = size(null);

Type annotations are required for the size function in Size.js because UseSize.js imports it and thus crosses the module boundary and isn’t inferred.

UseSize.js:6
  6: var result = size(null);
                  ^^^^^^^^^^ function call
  6: var result = size(null);
                       ^^^^ null. This type is incompatible with
  5: function size(input: string): number {
                          ^^^^^^ string. See: Size.js:5

Found 1 error

any Annotations#

any is a special type annotation that represents the universal dynamic type. any can flow to any other type, and vice-versa. any is basically the “get out of my way, I know what I am doing” annotation. Use it when Flow is getting in your way, but you know your program is correct.

Bottom Line#

You can type annotate all your code. That would be the most expressive and self-documenting approach. However, Flow does a lot of type inference for you to alleviate this requirement when it becomes a burden. The only place that you must annotate types is where those types go across module boundaries.

← Prev Next →

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