Javascript to TypeScript Migration: 5 Best Practices
October 9, 2021

JavaScript to TypeScript Migration: 5 Best Practices

Test Automation

As a GUI-focused BlazeMeter developer, it is very important for me to be able to understand and test all of our code, including our Legacy Code. That’s why I came up with the initiative to migrate our code from JavaScript to TypeScript. Changing our code to TypeScript helped us manage our code better, reduced our bugs, and enabled faster deployment.

Based on our lessons learned, here are 5 best practices when migrating your code from JavaScript to TypeScript.

Table of Contents:

Why Migrate Code from JavaScript to TypeScript?

Changing our code to TypeScript helped us manage our code better, reduced our bugs, and enabled faster deployment.

5 Best Practices For Going From JavaScript to TypeScript

1. Set Code Improvement Goals

TypeScript, JavaScript, and other languages or frameworks each have different advantages and are fit for different kinds of projects.

Once you understand that your current language doesn’t fit your needs, map out what those needs are, so you can be sure that the new language will answer them. It would be a shame to go through a whole migration project only to find yourself at square one.

We had two top goals. One, ensuring the GUI code functions are clear to anyone - experienced programmers who have been here from day one, as well as novices who just joined the team. Two, making the migration easy and the learning curve shorter for js users.

TypeScript’s advantages are many, with the main ones being its strict typing guidelines, the fact that it’s build on JavaScript, its maturity, the wide support available and the faster compilation it provides. These advantages answered our goals, so migrating from JS to TS was a good fit for us. 

2. Get the Whole Development Team on Board

As a team, we deliberated between two options: TypeScript (by Microsoft) and Flow (by Facebook). To decide between them, we demoed each one (I demoed TypeScript, and another team member demoed Flow).

We researched the differences between them, the team asked questions and at the end, we voted. For TypeScript, obviously.

This choosing method ensured the whole team was a part of the decision, that all concerns were addressed and that the decision wasn’t being seen as taken lightly. This helped with adapting TS and making it an integral part of our code development.

3. Migrate Your JS Files Gradually to TS/TSX

Rome wasn’t built in a day, and neither can a TypeScript migration be. Changing your code from JavaScript to TypeScript requires changing files with the .js extension to .ts or.tsx, adding signatures, building types and interfaces, and making sure nothing breaks in the process.

Instead of changing it all at once, we decided to switch gradually. The method we chose was that during each sprint, each developer would migrate a few files from js to ts/tsx, as part of their sprint tasks. This way, we ensured that our work wasn’t disrupted, developers didn’t feel threatened from the sudden change and our product didn’t suddenly break.

4. Create a Temporary Compiler for Both JS and TS/TSX

Because the migration was gradual, our system had to be able to deal with a middle stage where the code is made up of both JavaScript and of TypeScript. This caused problems in our compilation and our unit tests. Fixing that was up to me and I decided to create a temporary compiler for both js and ts/tsx. So here’s how I did it:

Webpack Configuration

Configuring webpack was easy, all I had to do was add awesome-typescript-loader to my loaders list:


{ test:/\.tsx?$/, loader:'awesome-typescript-loader', exclude:/node_modules/ },


and add:




to babel-loader.

Unit Testing

Mocha uses ts-node to compile typescript files, while webpack uses awesome-typescript-loader. Both act a bit differently since ts-node doesn’t support language features that are not a part of the ECMAScript standard. One of these was module imports. So, one of the most common errors I kept getting in tests was:
 TypeError: Cannot read property 'createElement' of undefined, even though at the top of the file I had:


import React from react;


After a lot of tinkering I found that changing the imports to the following style solved the problem:


import*as React from react;


It’s not pretty but it worked. However, changing the default imports in all the files was a lot of boring work and didn’t feel right. So back to Google. After a lot of research, turns out the solution was a simple flag change in the tsconfig.json file and changing the esModuleInterop property to “true”. Boom, all good.

Code Coverage

Babel coverage requires you to have:




While ts-node requires you to have both of them set to true. This was a bit tricky to get right, while the rest was basically the same. My solution was to simply separate the ts and the js coverage into different npm scripts, run them in parallel in another npm script, merge the output into one single coverage file and the report the output.

So it looks like this:

"tscoverage":"nyc --nycrc-path=.nycrc-ts mocha -R min ‘./app/**/*-spec.{ts,tsx}'""jscoverage":"BABEL_ENV=coverage nyc --nycrc-path=.nycrc-js mocha -R min ‘./app/**/*-spec.js’""coverage-report":"nyc report --include \"./coverage/{js,ts}-coverage/coverage-final.json\" --reporter=text-summary --reporter=cobertura --reporter=html --reporter=json-summary --reporter=lcov",
"coverage":"concurrently \"npm run tscoverage\" \"npm run jscoverage\"&& npm run coverage-report



Building to production worked out of the box! Phew!

5. Read A LOT about TypeScript

Read, read, and again, read, as much as you can about the TypeScript syntax and compiler. Migrating is a complicated project that can have many unexpected fallouts. The more you read the more you will be able to plan and avoid errors, and also react quicker and smarter to surprising outcomes.

Two recommended reads:

I read a lot and am still reading, and that’s part of what made our migration successful.

Migrating from JavaScript to TypeScript helped me understand our code much better, which in turn helped me develop smarter and more efficient code with fewer bugs. But a deeper code understanding has even more advantages. Wider knowledge of your code coverage enables writing better performance tests, gaining more insights from test results, and fixing bugs faster.

One more final tip: always run performance tests after committing code. BlazeMeter enables you to run performance tests as part of your continuous testing process, as well as high-scale load tests before big releases.



This blog was originally published on September 5, 2018, and has since been updated for accuracy and relevance.