Flow tips

Flow tips

By Guillaume Claret

Flow is a type checker for JavaScript developed by Facebook. By adding type annotations to JavaScript programs, we get the safety and the simplicity of typed programming languages while staying in the JavaScript ecosystem. At OuiCar, we have been using the Flow type checker for over a month with great success. We present here some tips we found useful.

Declarations for the DOM and React

You can read the Flow declarations for the DOM and React in the sources of Flow itself. This is also an excellent source to learn Flow by the example.

React events

We usually call the event handlers in React with some SyntheticEvent. These events are generic, so to get specific informations such as the target.value you need to do a dynamic cast as follows:

handleChangeEmail: (event: SyntheticEvent) => void = event => {
  // Or `HTMLSelectElement` for example.
  if (event.target instanceof HTMLInputElement) {
    const email = event.target.value;
    ...
  }
};

If you use ESLint, add the following directive to prevent warnings:

/*global SyntheticEvent*/

React components

If MyCompo is a custom React component:

class MyCompo extends React.Component {
  ...
}

then MyCompo is of type ReactClass and the JSX instantiation <MyCompo /> is of type React.Element. With ESLint, add the following directive to prevent warnings:

/*global ReactClass*/

Naming convention

We use t as the canonical name of the main type declared in a file. We borrowed this convention from the OCaml language (yes, the language in which Flow is written). For example:

// product.js

export type t = {
  ...
};

Thus, when we import the file product.js:

import * as Product from 'product';

function foo(): Product.t {
  ...
}

Sub-objects

If t is the type of an object:

export type t = {
  locality: string,
  street: string
};

then $Shape<t> is the type of the objects which include all or a part of the fields of t. For example:

{locality: 'Paris', street: 'du Sentier'}: t // OK
{locality: 'Paris'}: t // OK
{locality: 'Paris', street: 'du Sentier', country: 'France'}: t // error

Be cautious that the $ indicates that $Shape is experimental. As always, for ESLint:

/*global $Shape*/

Exact objects

By combining the type of an object and the type of a sub-object, we can get the type of the objects which exactly have the keys locality and street:

type location = {
  locality: string,
  street: string
};

{locality: 'Paris', street: 'du Sentier'}: location // OK
{locality: 'Paris'}: location // error
{locality: 'Paris', street: 'du Sentier', country: 'France'}: location // OK

export type t = location & $Shape<location>;

{locality: 'Paris', street: 'du Sentier'}: t // OK
{locality: 'Paris'}: t // error
{locality: 'Paris', street: 'du Sentier', country: 'France'}: t // error

Indeed, the type t represents objects of type location (with at least the keys locality and street) and of type $Shape<location> (with at most the keys locality and street). Thus t is the type of objects with exactly the keys locality and street. This trick can help to eliminate dead code, when objects grew larger than necessary.

Object with same keys

The type $Keys<t> represents the type of the keys of an object. For example, in our API, we can get replies with an optional error message for each parameter. If a request payload has a type:

export type t = {
  brand: string,
  price: number,
  ...
};

then the type of the errors would be:

{[key: $Keys<t>]: ?string}
OuiCar's Picture

About OuiCar

OuiCar is a community market place to find and rent cars anywhere in France. But we are not just car renters. We also like to experiment new technologies and share ideas with others.

Paris http://www.ouicar.fr

Comments