TypeScript vs FlowType
Apr 25, 2016
5 minutes read

While you're reading this, keep in mind that I'm available for hire stupid!

This weekend I played around with TypeScript. I’ve been using FlowType for a while in a couple of projects, but I wanted to make sure I wasn’t missing out on anything too awesome. Turns out I’m missing out on a lot. I didn’t end up switching to TypeScript (more on why in a moment), and I’m actually going to stick with FlowType for now, but I’m going to be keeping a close eye on TypeScript in the future.

Note that this all happened over the last few days, so as of right now, anything that I talk about is current and up to date. If you’re reading this in two weeks, it might all be outdated and invalid. Welcome to JavaScript.

Background

First, a little bit of background. I’ve been working with Nimblic for a while now on a product called Medtasker. It’s built around a standard called FHIR, which is a huge, very detailed specification. FHIR specifies a rich schema for medical (and particularly hospital-related) data, and we’ve used it as the basis for our data structures. Most of the Medtasker backend is written in Go, and all of the FHIR entities that we deal with are specified as structs. I take those structs and use Go’s reflect package to generate FlowType definitions. The syntactic similarities between FlowType and TypeScript meant that adapting that generator to spit out TypeScript definitions was very straightforward. That rich schema was my starting point - very different to the way things might look with a “hello world” project.

TypeScript

So yeah, TypeScript. The development experience using it with Microsoft’s VS Code is incredible. I should have expected this, as Visual Studio has always been a great piece of software, but I was still blown away by how polished the experience was. I write a lot of Go as well, and I always miss the nice code completion when I switch back to JavaScript. With VS Code, I had amazing, contextual completion and error reporting pretty much from the word… go.

TypeScript seems to handle polymorphic union types much better during code completion than FlowType does, and that made writing code with it pretty nice. The integration with VS Code was seamless, and there was no noticeable performance penalty using it. Even with a couple of thousand lines of type definitions and a monster union type made up of about forty very similar classes, I didn’t even notice it running.

I had about 75% of the codebase I was working with (which runs to about 2,500 lines of code) converted in one morning, and I was loving it. Then I found the first instance of the TypeScript behaviour that would ultimately cause me to abandon the exercise. Turns out that it’s not super strict about possibly-null or possibly-undefined values. Apparently this behaviour has tripped other people up as well. There is some activity (e.g. suggestion: non-nullable type and [request for feedback] nullable types) in this area, and a recent option called strictNullChecks suggests that TypeScript is getting closer to recognising whether something can be null or not.

Here’s a small example of the behaviour I’m talking about:

[ tryflow.org ] [ TypeScript playground ]

function greet(name: string): void {
  console.log('hello, ' + name);
}

greet('jim'); // TypeScript and Flow are both happy

{
  const person = 'jim';
  greet(person); // both systems still happy
}

{
  const person = 5;
  greet(person); // both systems sad
}

{
  const person: string = null; // Flow is already unhappy
  greet(person); // TypeScript has no problem with this
}

You might be wondering why I didn’t check that TypeScript did all the things I wanted before I set out converting a FlowType codebase to use it. The thing is though, I actually did look at what it could do. I read through a bunch of the documentation, learnt about how it does polymorphic object unions, and all sorts of other things. At no point in that process was it apparent that it had this particular behaviour. I kind of just assumed it would be there, since accessing properties on unexpected null and undefined values is by far the most common error I see in JavaScript codebases, and I figured it would be one of the first things a stricter flavour of JavaScript would tackle.

FlowType

After I gave up on TypeScript, I decided I’d update the version of Flow that I had installed and give Nuclide a spin. Nuclide is a Facebook thing, and integrates pretty closely with their React, Hack, and of course Flow projects. The integration between Flow and Nuclide is much better than what I get in Sublime Text (the editor I use most of the time), but still far, far behind the TypeScript and VS Code combo.

Flow doesn’t seem to be as good at code completion or providing type information to editors as TypeScript, but it’s still serviceable. It’s noticeably nicer than using regular old JavaScript, where my editor basically just uses tokens from the current file to figure out code completion. On top of actually not providing very helpful feedback, Flow can also be a bit of a glutton for CPU cycles. Whenever I save an interface definition, I hear the fans spin up on my laptop, and if it’s not plugged into the wall, I reckon the battery ticks down by a couple of percentage points.

The experience using Flow with Nuclide was pretty darned impressive for such a young project. As far as I understand, the core parts of VS Code have actually been around for quite some time, and the appearance of Atom (and atom-shell) just made it all that much easier to package into a desktop application. Nuclide on the other hand hasn’t event existed for a year yet. With time, the gap between TypeScript + VS Code and Flow + Nuclide might narrow a little.

Verdict

I’m going to stick with Flow right now. I’ll keep an eye on TypeScript, and if the situation around null changes, I could be persuaded to pick this experiment back up.


Back to posts