DEV Community

John Au-Yeung
John Au-Yeung

Posted on

Use Flow to Check Your JavaScript Types

Subscribe to my email list now at http://jauyeung.net/subscribe/

Follow me on Twitter at https://twitter.com/AuMayeung

Many more articles at https://medium.com/@hohanga

Even more articles at http://thewebdev.info/

Since JavaScript is a dynamically typed language, there’re going to be problems with variables and object properties having unexpected types. Also, some JavaScript type coercion rules are arbitrary. To make developing JavaScript apps easier, Facebook made extensions to JavaScript to add type annotations and type checking to the language.


Adding Flow to a Project

The language, which consists of Facebook’s extension to JavaScript along with the original JavaScript language, is called Flow.

We can easily use it in a new project. To add it globally, we just install Yarn and run:

yarn add --dev @babel/core @babel/cli @babel/preset-flow

Then, we create a .babelrc file and add the following:

{  
  "presets": ["@babel/preset-flow"]  
}

Then we can build and run the code:

yarn run babel src -- -d lib/

The command can be added to the package.json file as follows:

{
  "name": "project-name",
  "main": "lib/index.js",
  "scripts": {
    "build": "babel src/ -d lib/"
  }
}

We can also add it per project by running yarn add --dev flow-bin in our project, adding Flow as a development dependency for the project. Then we can run the flow command: yarn run flow (You may have to run yarn run flow init if you haven’t run it before).


Adding Flow Code to for Type Checking

Now that Flow is installed, we can add type annotations to our code to let Flow do type checking.

The simplest way to do this is to add type annotations after a variable to indicate its type:

// @flow  
let x: number = 1;

Notice that we have to add // @flow over our code to enable Flow to do the type checking of our code.

Also, we can add type annotations to function parameters:

// @flow  
function cube(x: number): number {  
  return x**3;  
}

Then when we write: cube(''); we get the following error:

[Flow] Cannot call `cube` with empty string bound to `x` because string [1] is incompatible with number [2].

This is because we passed in a string to a function that expects a number.

If we pass in a number, it should run:

cube(2);

Basic Types

Flow has its own type annotations for various data types that we’ll encounter in JavaScript.

Like JavaScript, it has various primitive data types. JavaScript has the following primitive types:

  • Booleans
  • Strings
  • Numbers
  • null
  • undefined (void in Flow types)
  • Symbols (since ES6, but not yet supported in Flow)

They’re available as literals or constructed with wrapper constructor or functions:

//literals
true;
"abc";
1;
null;
undefined;

// constructors
new Boolean(false);
new String("abc");
new Number(1);
// factory function
Symbol("abc");

Literal values have type names in lower case. Objects of types that are created with constructors have type names in upper case. For example, we can have:

// literals
x: number

// constructor objects
x: Number

Booleans

Booleans can be either true or false . We can write:

// @flow
function boo(val: boolean) {

}
boo(true);
boo(2);

boo(2); will give us the follwing error:

[Flow] Cannot call `boo` with `2` bound to `val` because number [1] is incompatible with boolean [2].

JavaScript converts objects to boolean when it’s needed. For example, it will convert it inside if statements or equality statements when we compare to booleans.

In JavaScript, we can write this:

if (1) {}

This is because it has truthy and falsy values. Falsy values are converted to false when conversion to boolean is done. The following values are falsy:

  • 0
  • undefined
  • null
  • NaN
  • empty string
  • false

Everything else is truthy.

With Flow, we have to explicitly pass in boolean values if we have a parameter or variable of type boolean or Boolean. boolean and Boolean types aren’t interchangeable with Flow. Given that we have:

// @flow
function boo(val: boolean) {

}

If we write boo(new Boolean(true)); we’ll get the following error:

Cannot call `boo` with `new Boolean(...)` bound to `val` because `Boolean` [1] is incompatible with boolean [2].

Numbers

JavaScript has only one type of number. It can be floating-point or an integer.

Flow has both the number and Number types. The number type is for numeric literals and the Number type is the wrapper object.

For example, we can write:

let x: number = 1;

Or we can write:

let x: Number = new Number(1);

However, we can’t write:

let x: number = new Number(1);

The code above will get us the following error:

[Flow] Cannot assign `new Number(...)` to `x` because `Number` [1] is incompatible with number [2].

We also can’t write:

let x: Number = 1;

This will give us the following error:

[Flow] Cannot assign `1` to `x` because number [1] is incompatible with `Number` [2].

Strings

Flow has the string and String types for the literal and wrapper object respectively.

It works like other primitive types. So we can write:

let x: string = 'abc';

Or we can write:

let x: String = new String('abc');

But we can’t flip the literal and wrapper object assignments.

Flow will only accept concatenation of strings and other strings or numbers. This means the following will work:

"foo" + "bar";  
"foo" + 1;

We can also concatenate a string literal with a wrapper object string. For example, we can write:

"foo" + String('abc');

However, we can’t concatenate a string with a String created with the String constructor. So the following will give us an error:

"foo" + new String('abc');

Concatenating string with any other types will get us errors.

null and void

Flow has the null type to represent the null value, and void to represent the undefined value.

So the following will work:

let x: null = null;  
let y: void = undefined;

Nothing else can be assigned to these types. This is the same for function parameters.

Facebook created Flow to let us check the types of data in our code for correctness. It prevents unexpected type assignments to variables and function parameters.

For primitive types, it has separate types for objects created with constructors for primitive objects and literal. For example, string is for the string literal and String is for strings created with new String(...) .

It also has the null and void types for null and undefined respectively.

Top comments (0)