Originally posted on my blog
Introduction
I won't hide the fact that I'm a fulltime Typescript developer, but I will try hard to give Flow a chance, because I really want a fair comparison between them. I know there has been several over the years, but this one, I'll try to update it at least twice per year.
I first met Typescript while working in my BD final project. Then, I have strong knowledge about Java and C#, but I didn't quite knew Javascript, so Typescript was something that interested me instantly. Anyhow, back then, I think that precompilers and build tools was just too much for me, so I did not work with it until mid 2016, when Angular came out.
And I met Flow last year, while I was working in a React project. We wanted to add static type check to the project, because it was getting big, and we weren't scaling that well. I try both of them back then, but I really wanted to use Typescript, because it have all this new syntax, so I did not give much credit to Flow.
I remember we, as team, chose Typescript, because its integration with several IDEs. I want to think things changed, and want to give Flow a chance, a real one this time.
I try to use a scale from 1 to 10 to evaluate the two of them in several features. At the end, I will just sum all the individual scores. However, I don't think the final score would be an indication of witch one is better of curse it will, if not why I'm even giving scores?.
What are they?
Flow is a static type checker for JavaScript, and Typescript is a language that strictly super sets JavaScript with optional static typing. This is a subtle difference between them, and I hope you notice it as we get down the road.
Getting Started
Flow
To start with Flow, we will need to install a compiler to remove the type annotations of the files. This will be either Babel, configured with Flow preset, or flow-remove-types
. I will go with the first one, as I think most of the projects now days use Babel anyway.
- We initialize the project, and add Babel dev dependencies, and Flow preset.
$ ~/flow# npm init -y
$ ~/flow# npm i -D babel-cli babel-preset-env babel-preset-flow
- Configure Babel and add a script in
package.json
.
.babelrc
{
"presets": ["env", "flow"]
}
package.json
(excerpt)
{
"scripts": {
"build": "babel src/ -d lib/"
}
}
- Install and configure Flow
$ ~/flow# npm i -D flow-bin
If you have a recent version of
npm
(^5.3.0) you could usenpx
, as I will. If not, you should add Flow as a script in yourpackage.json
and usenpm run ${flow script}
command.
$ ~/flow# npx flow init
$ ~/flow# npx flow
This will create a .flowconfig
containing a empty configuration, that uses a custom format that may resembles .INI
files.
[ignore]
[include]
[libs]
[lints]
[options]
[strict]
- Start Flow
$ ~/flow# npx flow
This command will start flow in watch mode in a background process and this will made the command took a long time to run. Just for a moment I though it was checking node_modules
. When it run a second time, it will run fast.
To stop the process use
npx flow stop
.
- Write a Flow file
src/foo.js
// @flow
function foo(x: ?number): string {
if (x) {
return x;
}
return 'default string';
}
The // @flow
comment is required to say what are the files that flow should check. Notice that even when this file is a Javascript, it not longer have a valid Javascript syntax, so it won't run on any browser, and that's why we need a tool to remove the types annotations.
If you want to check all the files, regardless they have the comment or not, you have to run
npx flow check --all
. Otherwise, just with runningnpx flow
will work.
- Checking the code
We run npx flow
again and it will tell that there is something wrong in the code:
$ ~/flow# npx flow
Error ------------------------------------------------------ foo.js:5:12
Cannot return `x` because number [1] is incompatible with string [2].
foo.js:5:12
5| return x;
^
References:
foo.js:3:18
3| function foo(x: ?number): string {
^^^^^^ [1]
foo.js:3:27
3| function foo(x: ?number): string {
^^^^^^ [2]
Found 1 error
- Compiling the code
In this guide I will use Babel but like I said earlier, you can also use the flow-remove-types
tool.
$ ~/flow# npm run build
Output: lib/foo.js
'use strict';
function foo(x) {
if (x) {
return x;
}
return 'default string';
}
As you can see, Babel emits the code even when it have a type error.
Typescript
To use Typescript you don't need to setup anything else, as Typescript includes its own emitter. However, you can setup Typescript with Babel 7, if you like, but I won't do it as the common use case is Typescript on its own.
- Initialize the project and add Typescript as a dev dependency.
$ ~/flow# npm init -y
$ ~/flow# npm i -D typescript
For this guide purposes I will install
Typescript 2.8
, that currently is inRC
.
- Configure Typescript
$ ~/flow# npx tsc --init
This will create a Typescript configuration file. It's a JSON standard file, with well JSON Schema support.
tsconfig.json
(excerpt)
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true
}
}
The tsconfig.json
file will contain almost all compiler options, with most of them being commented out.
- Start Typescript
$ ~/flow# npx tsc
This command will check and compile all of the Typescript files, and place a compiled Javascript file aside of it. We can change this behavior in the configuration, setting a source folder, a destination folder, and even prevent emitting if there was a type error.
To start Typescript in watch mode, just add
-w
flag.
- Updating Typescript configuration
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./lib",
"rootDir": "./src",
"lib": ["dom", "es2018"],
"noEmitOnError": true,
"strict": true,
"esModuleInterop": true
}
}
With this configuration we will have a behavior similar that we had with Babel, but if there is any errors in typing, it won't emit.
- Write a Typescript File
src/foo.ts
function foo(x?: number): string {
if (x) {
return x;
}
return 'default string';
}
Any valid Javascript code is valid Typescript code, but in order to be consider Typescript code, you need to place it in a Typescript file, with a .ts
extension. This fells like the comment in Flow, by default, Typescript compiler will only check Typescript files.
- Checking and compiling your code
Typescript have its own compiler, and it handle both, type checking, and code compilation. We run npx tsc
again and it tells us there is something wrong with our code.
$ ~/flow# npx tsc
src/foo.ts(3,5): error TS2322: Type 'number' is not assignable to type 'string'.
If you look, there is no lib
folder, as it did not complete the emit. The description is shorter, but the message is very similar.
Conclusion
I just show you how to setup the tools to a simple usage, but in a real project you probably would end using some code bundler like Webpack or Rollup.
In the setting up and getting I will give both of them the same score, 8. While I fell that Typescript is easier to setup, because you need to setup Babel too if you want to use Flow, you probably have Babel already installed and configured anyway.
I won't give anything yet for the code analyze, because the code was just too simple.
I will however, give 9 points to Typescript in the compilation, as I fell the prevention of emit some good feature, but I have to say I did like the Flow message better, that's why I will give it an 8.
Current Score
Feature | Typescript | Flow |
---|---|---|
Setting up | 8 | 8 |
Compilation | 9 | 8 |
Total | 17 | 16 |