Update 15 Jan 2020: Added link to the re-web CacheControl enum as a ReasonML example rather than having to defer only to Rust.
Why use a type system?
Ensuring that you’re using the right types for your data requires an enormous amount of discipline. Something not always available in an enterprise setting with pressure to ship. In a recent article about the Rust programming language (the current golden standard for type-safe systems programming), Microsoft highlighted that 70% of all bugs come from memory issues. These are prevented by Rust’s type-safety system.
A look at the options
Types are not bolted on top of the language but are made part of the language. This avoids some of the problems that Flow or TypeScript can have, such as types not matching the runtime. It also opens up powerful behaviours such as pattern matching.
ReasonML was created by Jordan Walke, the creator of ReactJS.
Level of type safety
It’s important that if we adopt a tool to enforce type safety it does not produce situations where a developer thinks they are secured by type safety when this is not actually the case. It’s better if the language is explicit about missing types rather than silently complying. It’s also desirable for a type system to err on the side of caution, requiring protection against a run-time situation the developer knows will not occur, instead of missing a run-time situation that actually occurs.
Since version 2.0 (now at 3.x) TypeScript has provided control flow based type analysis. This means that it uses control flow branches to narrow down the type to the most specific possible type. This helps especially for nullable types as it can help enforce that both the positive case as well as the null-case are handled.
In version 3.0 TypeScript introduced the
unknown type. This was introduced to fix issues that arose from the escape-hatch that TypeScript offered in the form of
any type will behave as all other types, essentially disabling type-checking. This can cause type-correct code that still causes issues at runtime. Just like
isArray, etc.). This makes it a safer default to use. If you adopt TypeScript you should probably disallow any.
Types for packages not written in TypeScript are often found in the @types npm package. However, there’s no guarantee that the type definitions of the package match the actual code. Mismatches here can still cause hard to trace bugs. Additionally if a package does not have types you can create your own by creating a package.d.ts file.
TypeScript uses structural typing. The idea behind this is that types are compatible if their members are compatible. See the TypeScript FAQ for more details on this.
Flow offers the any / Object / Function types which essentially opt out of type-checking. However, it allows to easily ban these types in your codebase on a file-by-file basis.
Flow also has a repository for packages that don’t have their own typings. React comes shipped with flow types as it’s a Facebook project.
ReasonML is fully sound and provides 100% coverage. Type safety is build into the language. An additional benefit of having type safety build into the language is that it allows us to use types to communicate business logic. For an example of this we can look at the documentation of Rust (similar to ReasonML) which shows a WebEvent and can at compile time tell us whether all possible states of this event are handled. ReasonML offers us these tools as well by not having a
null type but providing the
Option enum instead which can help enforce proper error handling. A more extensive ReasonML example of the power of enums with values is that of the CacheControl enum in the re-web framework, preventing non-sensical cache directives.
It’s easier to build on top of the work of others than to re-invent the wheel. A strong community behind the tools we use ensures that we do not need to fix all the problems that we encounter ourselves.
TypeScript has a large community. The GitHub project has 56.2k stars and is used by 1.6m other GitHub projects.
The flow community is smaller than the TypeScript community. On GitHub the project has 20k stars and GitHub says it has about 70k projects that use it on its platform.
The smaller community probably comes in part from Flow being used internally before being opened to the community. 2017 and 2018 saw little communication from the flow team to its community. They’ve done a better job since the start of 2019 with an exciting roadmap but the damage may have already been done.
Most notably the Jest testing framework decided to move from Flow to TypeScript to make it easier to onboard non-Facebook contributors. The PR where this move is discussed makes for an interesting read.
StackOverflow help will be available less but the Discord community is responsive and friendly.
For this evaluation we’re assuming that all development happens on OS X.
The community has centered around Prettier to serve as opinionated code formatter. This avoids many of the discussions around configuring ESLint rules to accomplish the same.
Type checking is only really useful if your IDE can tell you you’re doing something wrong while you’re doing it. Requiring a manual check each time code is edited will cause the type system to be quickly abolished by developers.
Open Social developers primarily uses the JetBrains IDEs so these will have focus here. However, VSCode is an editor quickly gaining ground with newer developers so it should be supported too.
TypeScript is a first-class citizen in VSCode as both tools originate from Microsoft. PHPStorm also has TypeScript support.
PHPStorm has good support for Flow. In the past VSCode support for Flow was more difficult. However, in the second half of 2019 Flow added support for the LSP language server that VSCode uses. Performance of type-checking has also been a large focus for Flow in this period.
Reason is supported in JetBrains IDEs as well as VSCode. However, the documentationrecommends using VSCode.
The learning curve for Flow is probably roughly equal to that of TypeScript. I personally prefer the layout of the Flow documentation and guide.
Getting rid of Flow is quite easy. Flow doesn’t provide its own compiler but has a Babel plugin that simply removes the Flow type-annotations. The output of this (without other transforms) can probably be used to automate Flow removal.
The maturity of a tool is an important indicator of how often the tool will change in breaking ways or how quickly best practices may change. These changes will require development effort on our part to adjust to or may prevent updating altogether.
Flow has been created by Facebook and is used in a lot of their internal projects (including React). This makes it quite mature. The size of the Facebook codebase means that the Flow team has to be careful with the introduction of backwards incompatible changes. It probably won’t go anywhere anytime soon, but shifts in the community can see its utility diminish as a tool for consumers outside of Facebook.
ReasonML itself is only a few years old (looking at the public repository). However, it’s build on OCaml which has a long and proven history. In 2017 50% of Facebook Messenger was converted to ReasonML. With those things in mind we can conclude that the language is definitely battle tested.
The talent pool for the type checking solution we choose is important. If the talent pool is small, it may be more costly to hire new talent to expand the team.
TypeScript is well understood by now. When looking at Google trends, the amount of interest in TypeScript is still lagging behind for now but steadily growing.
Onboarding a developer for TypeScript or Flow will probably involve the same amount of effort. However, there is anecdotal evidence that TypeScript is more popular among developers. It may be easier to find applicants when mentioning TypeScript, requiring a bit of explaining when mentioning Flow (this article could help).
Open Social’s Goals
We do not have any production front-end code using React at the moment. However, whatever we write now will probably have to survive for multiple years. The language should make it possible to write code that catches as many errors at compile time as possible.
Drawbacks to TypeScript and Flow are that they are solutions that are essentially bolted onto something that wasn’t originally created to create large robust codebases with. ReasonML elegantly solves this by approaching the problem from a different perspective: extending a stable type-safe language to be closer to what people are used to on the web.
Although ReasonML is the youngest solution in the list of options, it best tackles the goal of providing type safety and aiding in program validity. Having been created by the original creator of the React framework means that it’s a good fit for use with React. Being based on OCaml means that it can be compiled to native code when targeting mobile platforms.
The following sources, in addition to links included in the text, were used in making this document.
Jonas Bandi (April 6, 2018), Why you might NOT want to use TypeScript - Explains the drawbacks of TypeScript.
Jordan Walke (June 5, 2018), Why Reason got started (audio) - An interview with the creator of React on how ReasonML got started.
Marius Schulz (May 15, 2019), The unknown type in TypeScript - Explain the new unknown type in TypeScript and the difference to any
David Gomes (January 1, 2019) - Porting 30K lines of code from Flow to TypeScript