Flow and TypeScript
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)