So when I was introduced to TypeScript, I was sceptical. Why waste time adding so much boilerplate to define types? If you want to know a variable’s type, look at the context around it. The price seemed too high and the return too little.
However, over the space of 12 months, I did a complete 180, to the extent that I now think TypeScript should be the default choice on every new project.
In this post, I’ll share what made me change my mind, and explain why TypeScript is so powerful.
The First Cracks
The first cracks appeared when I was moved onto a huge React project at work. It was a maze of hundreds of components firing off events into the void to be picked up by redux sagas (which would then fire off more events).
My first task? The app’s performance was being affected because it was requesting too much data from the backend. But because the app was so big, no one knew what data was needed.
I was asked to follow the data from each response and see which fields were being used and which ones could be cut by the API.
It’s not hard to see how TypeScript could have helped with this. I could have opened the type definitions of the responses, commented out each field one by one, and seen if the compiler complained.
Instead, I spent weeks tracking the data through the app, across branching paths and sagas. And once I was done, I wouldn’t have been surprised at all if I had made some mistakes.
Even after I had moved on to other tasks, working on that codebase was a nightmare. There were massive data objects passed around the app with no up-to-date definition of what shape they were. There was no guarantee of what keys they would have or which keys were required.
In addition, the definition of those objects changed throughout the codebase, so even if you were familiar with the shape in one file, it was no guarantee an object with the same name in another file was the same structure.
There were helper functions that called helper functions that called helper functions. Props were drilled through several layers of components and abstractions. This meant finding what arguments functions required or what they returned would require digging through multiple files.
After a few months of working on the project, I began to think that maybe TypeScript could be useful after all. So I updated my opinion:
TypeScript is useful on large codebases which are worked on by multiple people or for libraries used by other people. However, for smaller codebases where you can be more familiar with the code, it isn’t worth the extra effort.
The Final Blow
A few weeks later, I went to make some updates to one of my side projects and I found myself facing the same problems. Even though I had written all the code, I was still having to spend time routing around to figure out what types I needed.
For example, I stored
date as a string in the database, but using it as a date object throughout the app. It wasn’t always clear where this conversion happened and, therefore, which format a
date variable would be in. The same thing happened with
amount, which I stored as a string in the database but used as a number in the app.
The time I spent having to read the context of the code to figure out what types were used began to outweigh the time I would have spent writing the code in TypeScript to start with. Not to mention the time spent tracking down bugs.
Changing My Mind On TypeScript
During this time, my project at work began to migrate the codebase over to TypeScript. I began to test some of my assumptions and saw that the costs of TypeScript were a lot smaller than I thought. I also began to see hidden benefits that I didn’t realise TypeScript would give. But that’s a blog post for another time.
In my next post, I’ll explain what I love about TypeScript and why I would choose to use it by default for every new project. Finally, in the third post in this series, I’ll look at common arguments against using TypeScript and discuss how valid those concerns are.