One of the Core Web team’s goals at Tumblr is to reduce the number of runtime issues that we see in our React codebase. To help move some of those issues from runtime to compile time, I evaluated the two leading type systems, Flow and TypeScript, to see if they could give us more type safety. I did a bit of background reading about the differences between Flow and TypeScript to see what the community had to say about them.

Background Reading

TypeScript, Flow and the Importance of Toolchains over Tools by Ben Teese

This post claims that Flow and TypeScript are similar enough that you should choose whichever of them is easier to integrate with your other tools. For Angular development, it recommends using TypeScript; for React, Flow.

TypeScript vs. Flow by Marius Schulz

This post claims that both TypeScript and Flow are equally good.

Flow vs. Typescript by Jan Varwig

This post outlines the author’s experience with using Flow in a React codebase. It advocates switching from Flow to TypeScript because of Flow’s unhelpful error messages, bad tooling, and propensity to spread untyped code. It also claims that most of the type annotations are able to be shared between Flow and TypeScript with only minor changes.

Type Systems for JavaScript by Oliver Zeigermann

This slideshow shows many differences around the philosophies and goals of TypeScript and Flow, and it gives detailed explanations in the differences between the two type systems. It explains IDE support and how to get access to third-party type definitions.

Lack of Consensus

It seems like many people have differing opinions about which type system is better for a React codebase. Because there wasn’t a broad consensus across the community, I decided to get some first-hand experience with each of these tools to see which one would be most practical and helpful for use at Tumblr.

Project Setup

I worked with a sample application to vet Flow and TypeScript. The application I used was Microsoft’s TypeScript React Starter. It uses a custom fork of create-react-app to get TypeScript set up. When testing out Flow, I used the standard version of create-react-app and used the source code from this exercise.

For the most part, Flow and TypeScript are basically interchangeable. I was able to reuse most of the source code between both projects with only minor changes. Here are some examples of changes I needed to make to get my TypeScript code working with Flow:

Flow requires that types are imported using import type where TypeScript re-uses import.

Some generic type constraints are different in redux’s type declarations between Flow and TypeScript, so I dropped the generic constraint for Flow.

Types cannot have the same name as constants, so I had to rename a few small things (see below).

Testing

After I got the project prepared I set up the following situations to see which tool performed better. These are my assumptions of the most common situations in which a type checker will help when writing React code on a day-to-day basis.

Handling an Unnecessary Case in a Switch

TypeScript

TypeScript realizes that 'not_real' is not a possible case for the switch.

Flow

Flow does not detect any issue.

Declaring Variables with Same Name as Type

TypeScript

TypeScript allows types to have the same name as constants, and it allows Command-clicking on the types to see their declarations.

Flow

Flow requires types and constants to have different names. In this case, I needed to rename the type to INCREMENT_ENTHUSIASM_T to appease Flow’s type checker.

Returning Incorrect Type from Function

TypeScript

[ts] Type '{ enthusiasmLevel: string; languageName: string; }' is not assignable to type 'StoreState'. Types of property 'enthusiasmLevel' are incompatible. Type 'string' is not assignable to type 'number'.

Flow 0.52

[flow] object literal (This type is incompatible with the expected return type of object type Property `enthusiasmLevel` is incompatible:)

Flow 0.53

[flow] property `enthusiasmLevel` of StoreState (Property not found in number) [flow] property `languageName` of StoreState (Property not found in number)

Missing Required Props When Instantiating a Component

TypeScript

TypeScript shows the error at the site where the properties are missing with the error:

[ts] Type '{}' is not assignable to type 'IntrinsicAttributes & Props'. Type '{}' is not assignable to type 'Props'. Property 'name' is missing in type '{}'.

Flow

Flow shows the error within the component where the property will be used, with no way to discover which call site is missing a property. This can be very confusing in codebases that have lots of reusable components. Flow displays this error:

[flow] property `name` of Props (Property not found in props of React element `Hello`)

Code Safety

TypeScript

TypeScript allows enforcing full type coverage on .ts files with the noImplicitAny flag in the tsconfig.

Flow

Flow provides a code coverage plugin so that you can see which lines are implicitly not typed.



Other Considerations

Flow has the most React community support and tooling, so there is much more documentation about how to get Flow and React working together. TypeScript is more popular with Angular developers. Choosing TypeScript may be breaking from community standards, so we may have more issues that don’t have a simple answer on Google.

Conclusion

I concluded that we should use TypeScript because it seems easier to work with. My experience seems to line up with this blog post. It has better error messages to debug type issues and its integration with VSCode makes coding more pleasant and transparent. If this ends up being the wrong choice later on, our codebase will be portable to Flow with some minor changes.

Shortly after arriving at this conclusion, Flow 0.53 was released and a blog post on Medium published touting it’s “even better support for React”. However, after running through the test cases above, I only found one case where Flow had improved its error messaging. TypeScript still seems like the more reliable, easier to use solution.

Further Reading

To continue our journey with TypeScript, I will need to integrate it into our codebase and teach it to the rest of our frontend developers. Getting started with TypeScript and React and Setting up a new Typescript 1.9 and React project look like they will be helpful articles when integrating TypeScript into our codebase. TypeScript Deep Dive looks like a great book for JavaScript developers that aren’t familar with TypeScript.

– Paul Rehkugler (@pr)