How TypeScript squashed all known bugs
By: Matt Eland
Tags: Programming, JavaScript, Quality Assurance, Typescript, Software Development

How TypeScript squashed all known bugs

You know that application that keeps you up at night hoping you won’t have to change it? It seems like we all have one. Matt Eland Follow Sep 22, 2019 · 4 min read

Mine was in JavaScript. It was a single page application made with just jQuery and JavaScript and it was the most brittle thing I’ve ever worked with.

Let me tell you how switching to TypeScript fixed all known bugs with the application while preventing entire classes of defects from ever popping up again.

Finding Existing ‘Linking’ Issues

The first benefit we saw when cutting over this application was that it was immediately apparent where methods were being called with too many parameters or too little, or in the cases where method names were blatantly misspelled (this was a large and undocumented codebase without many experienced individuals working on it or standardized processes).

By forcing everything to go through tsc (the TypeScript compiler), methods that didn't exist stopped the program flow in its tracks.

Stringly-typed Variables

We had a number of variables which were being compared against numbers in some places. That’s okay-ish if you’re using == comparison where JavaScript tries to convert between stringly-typed numbers and actual numbers, but if you even tried to use === (which you should - for safety and for performance), you'd quickly find that '1' === 1 would be false and you'd have a lot of errors.

Bringing order through type declarations

By declaring all of our parameters and variables with explicit types (using : number for example), we were able to catch these issues as well.

There were a few stragglers — places where we declared the value as a number, for example, but it actually read in as a string from the user interface, but these weren’t too hard to track down once we figured out what type of code to look for.

Catching silly mistakes with ESLint

We used ESLint to lint our TypeScript code since TSLint indicated at the time that it was being deprecated sometime in 2019.

ESLint allowed us to catch probable issues such as return values not being assigned to a variable or other correctness issues. Since we incorporated ESLint into our build process, this meant that any time we built we’d catch any new linting errors.

Introducing Classes

Additionally, we augmented ESLint with Prettier for code formatting, giving us a consistent code style without us having to worry about it too much.

Because we had the transpiler to catch any blatant issues and linting to catch any new goofs, we felt safe to start taking our loose JavaScript functions and move them into classes, bringing a lot more organization and standardization to our TypeScript and highlighting more opportunities for code reuse and consolidation.

Eliminating Global State

Because we introduced classes, we had to start moving state out of global scoping and into individual classes that were responsible for it.

It turned out that behind blatant method parameter mismatches and comparing values of different data types, that poor state management was the next largest cause of bugs in the application.

While we didn’t have the time on this project to introduce a solution like Redux or other related frameworks, the very act of moving state into individual classes turned out to be good enough to give us the control we needed to find the improper state manipulation code and fix it.

Testability

Taking a large nest of spaghetti JavaScript and chopping it up into TypeScript classes also allowed us to write Jest tests around individual classes, giving us a further degree of safety and confidence while making changes to the application.