Engineering

All about boosting customer happiness with science and code

❮ Blog Home
Wootric + TS

Why We Rewrote Our JavaScript SDK in TypeScript

November 24, 2020 By Marín Alcaraz & Ray Sohn
TypeScript forced us to document all of our assortment of feature flags and switches. We have a lot of flags for various features, and by using interfaces we can no longer have mysterious undocumented flags. All flags must be in the interface or else the compiler will warn us. This keeps developers honest.

At Wootric we've been fans of Typescript for some time. Anders Hejlsberg, the lead architect of TypeScript has a legendary record of working on amazing languages such as Turbo Pascal and C#. A long term dream of the team was to move our JavaScript snippet to TypeScript. Our JavaScript snippet is what renders our pop-up survey in browsers. And earlier this year we started experimenting with ways we could achieve this. There were some hurdles and mistakes but we managed to push through those and now the snippet is being compiled from TypeScript to JavaScript. We have types!

Keep reading to learn more about this journey.

Why We Rewrote It

In the software industry, doing a full rewrite is usually frowned upon and considered a waste of time but plain JavaScript just wasn’t cutting it anymore. Not everyone on our team is a JavaScript expert and there are a lot of gotchas in JavaScript. Moving to language with types would alleviate a lot of the mental burden and make sure that basic interfaces were adhered to. We like to think of the type checker as an automated proofreader, making sure you dot your i and cross your ts before shipping out production software that is used by millions. According to this study, 15% of JavaScript bugs can be detected by TypeScript. 

Migrating code to a new language is usually very difficult but since TypeScript is a superset of JavaScript this made the transition much easier than translating to a language with a wildly different syntax. Another feature of TypeScript that made the migration easier is the “any” type which serves as a placeholder for a type. We are slowly moving towards not allowing “any” but the silver lining is that “any” type allowed us to migrate an entire codebase without having to add types to everything. The TypeScript compiler acted as our proofreader and filled in the types until we got around to adding them.

From improving our build infrastructure to giving us guarantees thanks to its types, these are the problems we had and TypeScript helped us fix:

  • Maintaining the Almond.js compiler infrastructure was cumbersome. Different builds meant different files and different tests. We also had to write a lot of plain text on those files. That text was later evaluated to be bundled as JS due to the AMD module system.
  • Keeping 3rd-party libraries up to date was a manual process. Now we use package.json and simply import the libraries using an import statement. In the past, we were directly pulling 3rd-party files from our local repository to bundle them into the snippet, if we wanted to update a library we needed to update the file by hand. Now we run `yarn upgrade` and we are ready to go!
  • We needed to improve our old browser support and have guarantees around it. TypeScript has a compiler flag that lets us target older browsers. Thus we can guarantee to our customers that our SDK works in ES3 and up browsers.
  • Several bugs that had been in the original code for a while:
    • The compiler found a function that was supposed to return a boolean but wasn’t returning anything. We were using this function in an if statement and the compiler warned us that the function wasn’t returning a boolean.
    • By using interfaces we discovered that our configuration contained a duplicate configuration object inside of it. Using recursive interfaces we were able to define this unusual property.
    • After adding type signatures to our internal priority queue, the compiler told us that some of our functions had return value mismatches. Some parts of our code were expecting Promises and weren’t getting them. This led us to discover a serious bug in the code.

Conclusion

You can't just pick new languages and force them into your stack. You have to measure the trade-offs and take a look at the pros and cons that the new tool will bring. In our TypeScript projects these are the results and we think the trade-offs are worth it.

Pros

  • Smaller output size which is great since we want our JavaScript SDK to be as lightweight as possible so we don’t interfere with our customer’s app performance.
  • TypeScript forced us to document all of our assortment of feature flags and switches. We have a lot of flags for various features, and by using interfaces we can no longer have mysterious undocumented flags. All flags must be in the interface or else the compiler will warn us. This keeps developers honest.
  • Increased maintainability because the SDK was getting large enough that making big changes was very scary and we might cause regressions. Having everything typed, we can refactor with more confidence.
  • Not having to write semicolons is hard at first but after a while, you wonder why you had to write them in the first place.
  • @types is mature now, and every library we needed had types registered with Definitely Typed.
  • Often overlooked and underestimated, having a code base that makes your job easier and more fun is a win for the engineering team. TypeScript has excellent tooling and we are very happy about that.

Cons

  • The build time has gotten slower but we’re starting to fix that using guides like this one.
  • Developers need to ramp up with a whole new language, tool-chain, and conventions.
  • A new set of problems. After being familiar with the JavaScript version of our snippet  we often knew where to look when things broke with just a few pointers. Since migrating to TS we encountered one bug that was directly related to the switch and it took us some time to figure it out.

Filed Under: Engineering