Migrating from Flow to Typescript

Experiences with migrating a small library

Why migrate to TypeScript?

I recently wrote a post about how I tried out FlowType only to feel a little less enthusiastic about it by the end.

At the time I wasn’t on the TypeScript bandwagon but TypeScript kept popping up frequently enough on my feeds that it started to feel negligent not to at least try it.

As a result, I decided to convert my Genetic Algorithm library which was originally written in Flow.

A genetic algorithm in action

At the time of writing this the library was about 450 lines of Javascript. That’s small enough that I could migrate the entire thing in one sitting, yet the library is non-trivial in functionality and in its usage of types.

Migration Experience

No more Babel

In my previous post on Flow one of the reasons for not immediately flocking towards TypeScript was because TypeScript is a self-proclaimed superset of Javascript.

At the time I didn’t see the value in that self-proclamation. What I failed to realize is that if TypeScript is a superset of ES6, then that means I don’t need Babel and that removes a handful of npm dependencies as well as some build configuration.

As a result I deleted my .babelrc file, removed a bunch of dependencies, and cleaned up my npm build scripts.

package.json

Config differences

My old .flowconfig file was minimal to begin with.

That file was replaced by a tsconfig.json file that had a few more lines in it.

The documentation for this file was sufficient that it didn’t take me long to get things wired up.

The only part I really got hung up on was when I set module to ES6, instead of commonjs. That caused me a bit of heartache when running the Hello World example on Node, but it didn’t take long for a couple Google searches to unblock me.

Syntactical differences

One of the really great parts about this migration is how incredibly similar the syntax is between these two type solutions. I was able to convert everything to TypeScript within 90 minutes, including usages of union types and generics, and that was with only having mild familiarity of TypeScript to begin with.

The only noticeable difference worth mentioning was the difference in import statements.

hello.world.js (flow)

..compared to TypeScript’s import syntax

hello.world.ts

With Flow, type imports are separate from other imports from the same file while TypeScript collapses it all into the same import statement.

Type checker differences

The only differences I could possibly comment on are potential type errors that TypeScript’s compiler would have caught that Flow’s didn’t. There are likely other differences but since I already wrote the project in Flow most of my potential type errors were already eliminated.

As it turns out, there was a single type error Flow did not catch that TypeScript did.

In the above code maybeOrganism.score is defined as a number | null whereas currentBestScore is implicitly defined as a number.

The problem here is if maybeOrganism.score is actually null then a null value is assigned to an inferred number type. This is bad and TypeScript’s compiler caught this one.

That’s not to characterize Flow’s type checker as bad. Flow caught a lot, but this one slipped through the cracks I suspect because I didn’t explicitly declare currentBestScore as a number and perhaps Flow assumed it was an any type.

Error message differences

One of my biggest gripes with Flow was its jarring error messages. Here’s an example again from my last post.

Though technically this error message has everything a developer could possibly want, it actually has too much and not in an at-a-glance format. The Developer Experience is not all that great.

Here’s the kind of error that TypeScript gives.

Although this error message still has some noise in it, the message is having me look at a single line in my source code and it succinctly tells me the error. I can quickly glance at the error and figure out what’s wrong which leads to a much better developer experience.

Problems

The only problem worth reporting is that VSCode’s Intellisense wasn’t working unless the version of TypeScript in the project matched exactly the version of TypeScript that VSCode uses.

I’m not a fan of the idea of maintaining two different versions of TypeScript but this is less of a problem with TypeScript and more of a criticism of how VSCode handles TypeScript.

This isn’t a problem after all

Community

Lastly, I think it’s worth mentioning community. The fact is TypeScript is noticeably moving forward more quickly than Flow and it’s building up a community as it goes.

Angular2 uses TypeScript. VSCode has built-in TypeScript support. TypeScript is well-documented. TypeScript questions are being answered on StackOverflow. Most impressively TypeScript has released a new minor or patch version at least once a month for almost a year now.

It’s clear that TypeScript has momentum and I see no sign of that momentum dwindling any time soon.

Is TypeScript better than Flow?

Let’s be clear about something. This post is insufficient to answer that question.

Flow was sufficient as a type solution for my library and it turns out TypeScript is too. Both have their trade-offs and I suspect both will reveal more conclusive thoughts as I dive into more complicated use cases.