Immutable Records & Redux with Flow/Typescript
I recently designed and developed a small javascript application using React and Redux. After hearing a lot of my peers talk more and more about Flow and Typescript, I knew that I wanted to give one of those options a try. I ended up choosing Flow. My reasoning at the time was that I felt like Typescript was an “all-in” approach while Flow felt like something I could introduce gradually (Oh how wrong I was!)
After finishing the initial MVP, I saw a lot of benefits from using Flow, but I was also turned off by a few issues. The next project I started I used Typescript, and immediately fell in love, so when I revisited my Flow project, I refactored it into Typescript (It was small enough that it just took an afternoon).
The application was your classic React/Redux application, with a single file describing the state. The application was a simple one — it presents the user a question, and logs the response of the answer clicked. I opted into using ImmutableJS Record
s to describe my state, because I really like how they are truly immutable, but allow you to access the properties as you would any javascript object.
State
When interacting with the state, I wanted to make sure that I wasn’t doing anything silly with it — only expected mutations on expected properties.
Flow
Typescript
What benefits do I get out of defining types along with my state?
My state is clearly communicated and documented within my code.
If a new developer were to be introduced to the repository, they’d be able to reference my type definitions to understand the expected shape of the state.
I can’t (accidentally) mutate the state unexpectedly
In my reducer, if I misspelled a property when mutating the state, Typescript would let me know:
Actions
When dispatching an action, you want to make sure you are only dispatching actions that are expected. In your reducer, you only want to handle actions that are expected.
Flow
Typescript
What benefits do you get for defining your types like the above example?
Type safety in your reducer
If you try to handle an action that isn’t defined, Typescript will let you know.
My thoughts on Typescript vs Flow
I wanted to touch on why I moved from Flow to Typescript in this application.
For one, the project was small enough to be able to handle a transition like this and not have any issues. I think for any medium-to-large sized project, a migration like this would be impossible in one go. You’d have to concoct a strategy to migrate over time.
As far as Flow goes, I think it’s a good tool but it feels fragile to work with. What do I mean by that? I found that the integrations to VSCode (or any other IDE) to be lacking. It felt like Flow was slow to respond with valuable feedback when I wrote a piece of code that broke a rule, sometimes 10–20 seconds after the fact. This lead to annoying tail-chasing. On the other hand, Typescript’s integration with VSCode is fast. I felt like the IDE would respond immediately with issues as I typed.
Flow gives you a nice tool that shows you code coverage, but in the end it just frustrated me more than helped me. I’m a completionist at heart, so having green lines throughout my codebase like this drove me crazy.
Finally, it feels like Typescript has a much better community backing than Flow. I used glamorous
and glamor
in this project to render UI components, and there is no flow-typed definitions for the library, but there is for Typescript. This seems to be a common occurrence for a lot of libraries that are used often but are not ubiquitous.
Thanks for reading my article. Do you prefer one over the other? Why? Post in the comments below!