Introduction à Flow.js

publié le

Flow est un analyseur statique de type pour JavaScript, créé par Facebook pour ses projets (comme React ou Jest, 2 excellents outils pour développeur Front). Son principal adversaire et pas le moindre est TypeScript de Microsoft. Sans entrer dans les détails car ici on ne va parler que de Flow, TypeScript est un méta-langage qui englobe JavaScript en lui rajoutant des “options”. La plus connue est le Typage qui est la partie qui nous intéresse dans cet article. Mais TypeScript apporte aussi les Annotations/Décorateurs (qui vont arriver dans ECMAScript 2019 ou 2018 : actuellement en stage2), les futures fonctionnalités définies dans les versions d’ECMAScript, et un compilateur pour faire fonctionner tout cela dans nos navigateurs actuels.

Flow n’est donc opposé à TypeScript que sur une des fonctionnalités de ce dernier, en contrepartie Flow fonctionne avec JavaScript c’est à dire que vous pouvez l’insérer progressivement sur votre projet qui était démarré avant même que TypeScript n’existe.

Si vous avez déjà jeté un œil à Flow, vous pouvez vous arrêtez votre lecture ici car cet article ne vous apportera rien vu qu’il s’agit d’une présentation triviale. Un article plus avancé verra le jour dans les semaines à venir.

Fonctionnement

Pour utiliser Flow vous avez besoin :

  • de l’installer
  • de paramétrer votre code
  • de l’utiliser
  • de nettoyer votre code des paramètres ajoutés pour l’exécution

Basiquement si vous prenez n’importe quel projet JavaScript vous pouvez ajouter la ligne "flow": "flow" dans la section scripts de votre package.json puis lancer les étapes 1 et 3 :

npm i -D flow-bin
npm run flow init
npm run flow

Et la magie opèrera normalement en ne remontant aucune erreur vue que nous n’avons rien fait pour la partie 2.

PS : L’initialisation n’est bien sur à faire qu’une seule fois.

En mode développement

Il est bien sur hors de question de lancer une commande npm aussi longue (analyse tout le projet à chaque fois) pour chaque sauvegarde et le but même d’un analyseur statique de type est de remonter les erreurs en temps réel dans l’éditeur de texte. Flow fonctionne donc par analyses incrémentales à partir d’un processus en tache de fond.

npm run flow status
No errors

A partir de maintenant Flow observe les modifications dans votre projet pour remonter les erreurs. Vous pouvez arrêter le processus avec npm run flow stop

Paramétrer son code

Pour demander à Flow de contrôler le type d’un fichier il suffit de lui ajouter en commentaire l’annotation @flow

// @flow
// ou
/* @flow */

A partir de maintenant votre fichier est validé par Flow automatiquement et l’ajout d’un code invalide remontera les erreurs :

// @flow

function square(n) {
    return n * n
}

square("a")

Constater l’ampleur des dégats

Flow propose de valider les fichiers même s’ils ne contiennent pas l’annotation @flow :

npm run flow check --all

Cela permet d’avoir une idée de la longueur du chemin que vous aurez à parcourir pour avoir un projet clean. Sur mon (petit) projet test ça a donné ça :

Found 32 errors

Et oui l’ajout de controle de type à posteriori ne fait pas que du bien, mais c’est justement ce qui est agréable avec Flow c’est que c’est incrémental.

Utiliser Flow

Flow utilise beaucoup l’inférence de type pour remonter les 32 erreurs évoquées un peu plus haut, mais il permet aussi de définir des types explicitement :

  • Primitive Types : number, string, boolean, null, undefined(void), Symbol
  • Literal Types : une forme de validation par énumération de valeurs (2 | 3) n’admet que les valeurs 2 ou 3 et pas les autres nombres
  • Any Types: comme en TypeScript, à éviter
  • Maybe Types: Un type optionnel préfixé par ? peut être null ou void.
  • Function Types: signature de méthode = typage des paramètres et de la valeur de retour
  • Object Types: Typer les propriétés d’un objet, fonctionne avec l’inférence
  • Array Types: Typer la nature du tableau
  • Tuple Types: Un Array de taille fixe, et dont les cases sont typées
  • Class Types: POO avec héritage, Generics and co
  • Type Aliases: l’interface de TypeScript (c’est à dire la définition d’un objet dont les props sont typées mais n’admettant pas de méthode)
  • Interface Types: Les interface comme en Java
  • Generic Types: Les Generics/Diamonds comme en Java
  • Union Types: énumération de type string | number | Type1
  • Module Types: typer l’export, pour que l’import le soit par inférence

Et quelques autres plus marginaux. En gros Flow vous apporte une validation qui par inférence va se débrouiller pour trouver le maximum d’erreurs possibles. Il va vous demander de signer vos méthodes quand elle ne le seront pas au moment ou vous allez mettre @flow dans votre fichier. Et il va vous donner une boite à outil qui vous offre le même contrôle de type que le plus typé de tous les langages objets typés : Java, mais de façon optionnelle.

Enfin pour utiliser tous ces types on utilise le caractère : oui comme en TypeScript :

// @flow
var a: number = 2

function square(n: number): number {
    return n * n
}

Type Vehicle {
    nbWheels: number,
    engine: string
}

// ...

Librairies tierces

Comme les typings pour TypeScript, Flow demande aux éditeurs de librairies tierces de décrire un fichier flow-typed pour simplifier la vie des projets utilisant Flow. Cependant Flow ne lancera aucune insulte si le fichier flow-typed n’existe pas, il se contentera d’utiliser l’inférence ce qui conduira à remonter peu d’erreurs.

Nettoyage du code

Vous l’avez surement remarqué à la lecture de cet article, Flow utilise des syntaxes qui n’ont pas grand chose de valide en JavaScript pour remonter des erreurs à l’analyse statique, maintenant pour fonctionner, pour utiliser concrètement ce code il va falloir rajouter à votre stack de développement (grunt/gulp/browserify/webpack/node/…) une étape de clean-up de Flow. Il y a des tutos un peu partout pour mettre en place Flow sur un projet React, un projet Vue, un projet Node, …

#installation
npm i -D babel-cli babel-preset-flow
{
    "//": ".babelrc",
    "presets": ["flow"]
}

#nettoyage avec résultat dans le dossier lib
npm run babel src/ -d lib/

Conclusion

Flow est une alternative à TypeScript pour la partie Typage, il vient avec ses propres contraintes et un avantage : il est beaucoup plus facile de faire de la migration incrémentale de sa base de code JS vers Flow que vers TypeScript. En contrepartie il offre moins de fonctionnalités que TypeScript, et il faudra chercher du coté de Babel pour avoir le même niveau de features (par exemple pour intégrer les Decorators). Flow se présente comme un simple linter là ou TypeScript est un outil capable de transformer votre éditeur en IDE. Pour faire simple on a 2 acteurs rendant un service similaire et qui vont probablement activement évoluer pour être le meilleur : saine concurrence. Cette concurrence va permettre d’enrichir les futurs débats du TC39 pour l’arrivée des Types dans ECMAScript (car oui après-demain on aura les Types directement dans le langage: actuellement en stage -1 peut-être même -10)