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.