DEV Community

loading...

TypeScript vs Flow - Getting Started

michaeljota profile image Michael De Abreu 惻6 min read

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.

  1. 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
  1. Configure Babel and add a script in package.json.

.babelrc

{
  "presets": ["env", "flow"]
}

package.json (excerpt)

{
  "scripts": {
    "build": "babel src/ -d lib/"
  }
}
  1. Install and configure Flow
$ ~/flow# npm i -D flow-bin

If you have a recent version of npm (^5.3.0) you could use npx, as I will. If not, you should add Flow as a script in your package.json and use npm 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]
  1. 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.

  1. 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 running npx flow will work.

  1. 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
  1. 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.

  1. 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 in RC.

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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

Discussion (0)

pic
Editor guide