JavaScript vs. TypeScript vs. ReasonML

In this blog post, I describe the pros and cons of three programming languages/dialects: JavaScript, TypeScript and ReasonML. My descriptions are based on recent experiences with TypeScript and ReasonML on a few smaller real-world projects and on years of experience with JavaScript.

Before we take a look at the languages, let’s first consider the pros and cons of static typing, given that TypeScript and ReasonML are statically typed and JavaScript isn’t.

Documentation: For most code, I find it very helpful if the types of parameters are documented. Then I know what to expect as a caller and as a callee.

But it goes further than that. When I revisited an old JavaScript code base to add static types, I had forgotten how it worked. With the types, it is now laid out much more clearly how everything works. For example, in order to add type annotations for the parameters of a function, I often had to visit its call sites. That is, the information “how is this function used?” is hinted at by the types.

I’d also consider auto-completion in editors as “better documentation”. It means you have to consult docs much less when working with an API (good doc comments help, too). For example, when I started doing more DOM programming in 2006, I did it via GWT (which is based on Java and was an attractive solution back then, for several reasons). Exploring and learning the DOM API was fun, due to auto-completion in Eclipse.

A quick way to check the types of parameters. I knew I was ready to try static typing in JavaScript once I added programmatic type checks to several functions – that code felt like unnecessary boilerplate.

It helps with refactoring (adding cases to enums, etc.).

Cons:

It takes time to learn.

It is an additional layer of complexity. You are basically writing the code again, on a different level.

It constrains your freedom of expression. Things can get complicated if you get into generics, co- and contra-variance (e.g., an Array of strings is not a subtype of an Array of objects), etc.

It does not prevent defects. At least that’s what current research says. In my experience, you do catch a certain class of errors (e.g. missing null checks). But I may have caught them even earlier, if I had written unit tests instead of adding static types. In order to detect serious defects, you need tests.

You lose some interactivity and compiling takes time. On the other hand, it’s become almost impossible to avoid compilation in the JavaScript ecosystem.

If you want to experience the best of what static typing has to offer, use ReasonML (or OCaml, which it is based on). For example, almost every language feature is designed so that you need the least amount of type annotations (by supporting type inference).

It is impressive, how far along everything already is (especially the editor support), but several important pieces are still being worked on: better support for Promises, iteration and async iteration; an improved standard library called Belt; improved JavaScript interop (which is already decent, but still more complicated than I’d like); better support for Unicode strings.

ReasonML has impressively short build times. It’s considerably faster than TypeScript, which is a definite usability advantage.

It has some bindings for JavaScript libraries, but the selection is still limited: see Reason Package Index.

You have the option to go native. For example, Jared Forsyth has written the game Gravitron in ReasonML that runs natively on Android, iOS, web and macOS.

TypeScript’s type system is more lightweight than I expected it to be. It feels more like FP than like Java. For example, it works structurally and not nominally: If you create an interface, that interface “matches” all objects whose shape is described by that interface. You can introduce interfaces at any time, without having to touch existing code.

Personally, I’ve started to use some kind of static typing once a project grows beyond a certain size (or if I expect it to eventually grow that big).

The strength and variety of the JavaScript ecosystem is amazing at the moment: You can switch between JavaScript, ReasonML and TypeScript, as needed (and with varying degrees of work). They share some tools, many libraries, and much syntax. For example, when I needed a quick templating solution in ReasonML, I used the JavaScript-based EJS library, via npm.