Three Tips for Migrating from JavaScript to TypeScript

Over time, and especially in the past few years, the tech community has gotten sick of JavaScript and its loose typing. Trying to write a complicated program without static types is like driving at night without headlights—you’ll probably be fine for a while, but there’s a good chance you’ll crash a few times along the way.

Type systems give us guiderails to protect ourselves from the trees along the road. Types can help make refactoring painless. Types are a good thing, we have learned. “Alas, give us types in the browser!” we say.

There are many typed languages that compile to JavaScript, each with their own pros and cons. However TypeScript has recently become a frontrunner in the “JavaScript alternatives” realm, and we have started using it in a few projects at Atomic.

I will not be outlining all the reasons why I think TypeScript is great, but one reason is how easy TypeScript is to integrate with an existing JavaScript project. When faced with the challenge of refactoring a codebase from JavaScript to some-other-language-script, ease of adoption is huge. This is where many alternative languages fail.

However, despite the relative ease of adoption, I have a few tips that should help you capitalize on your investment.

1. Have a Plan

It’s easy to get excited about a new tech stack and just dive in head-first. While this works well for experimenting and learning, it is not sustainable. Without careful planning, it’s easy to spend a lot of time refactoring code that doesn’t actually need to be refactored.

JavaScript isn’t inherently evil; it is just inherently error-prone. It’s much better to target particular modules that will give you the best bang for your buck.

Ultimately, the goal of migrating to TypeScript should be to add types to the places that you use the most, to make sure that you don’t mess up. For example, start by finding core modules that are used throughout the code (especially new code) and focus on them.

2. API First

Once you’ve figured out where to begin your refactor, start refactoring at the highest possible level. Find the highest level APIs for that module and simply add their types. Using interfaces, classes, types, etc., define the intended behavior of these functions along the boundary. By simply adding these top-level type signatures, any new code that is written to use these APIs can benefit from the types.

Once you have added types to the high-level API, you can decide where it makes sense to move to next. But make sure you are always evaluating the cost-benefit ratio on your next targeted file. I repeat: JavaScript isn’t inherently evil; it is just inherently error-prone. It is completely valid to leave a file in JavaScript if there is no real benefit in converting it to TypeScript.

3. Start Small

The process of migrating a single file from JavaScript to TypeScript can be a little tedious and cumbersome, especially as you are becoming familiar with the language and toolchain.

Start by changing the file to a ts (or tsx) extension and fixing the compiler errors in the simplest way possible–often, that’s just defining any types for everything. Getting the compiler happy and committing to VCS as quickly as possible will help with merge conflicts, especially on a larger team.

Once the file is happily converted, you now have the flexibility to migrate the rest of the file as needed. The process of migrating a single file might take days or even weeks, so the ability to incrementally migrate a file is extremely advantageous.

Overall, the past few months of working with TypeScript have been a blast. Its ability to incrementally integrate into exiting codebases has proven to be a huge benefit for my project. I hope these guidelines will help you with your next project, as well!