If you’ve been working in the software development industry (specially Frontend) in the past couple of years, it’s most likely you worked, or still do, in a project in JavaScript. And by now, you’re more than tired from it. Therefore, in this article I would like to talk about  how to migrate from JavaScript to TypeScript.

Don’t get me wrong, JavaScript is an extremely flexible and easy to learn and use language, with one of biggest communities out there. But it also comes with a bunch of pitfalls you eventually end up running into, like it’s loose typing “odd” behaviour, per instance. And there are some really interesting languages that transpile to JS, thus can be used on top of it, such as Dart, Elm, or a widely adopted TypeScript (in part thanks to Angular), to name a few.

 

How to easily migrate from JavaScript to TypeScript

 

It’s much simpler to abandon JS when starting a new project, where you don’t have to worry about things like retro-compatibility, or app’s maintenance in production. In that case, you can try many alternatives, and choose the one you like the best.

Nevertheless, if you must continue to work on an old project, you can begin to migrate from JavaScript to TypeScript, in a “slow” manner, file by file, or adding TypeScript only in the new files you create.

This is possible because TypeScript is a superset of JavaScript, meaning that any JS code is also a valid TS code (assuming the TS configuration is set to be compatible with it).

Now, I will present a simple way to add TypeScript to a project, without the need to modify our configuration of webpack, gulp, or whatever our build system is, nor deploy system.
Assuming you use npm as a package manager in your project, first thing we need to do is to add TypeScript as a dependency (if not, you can install it globally):

npm install --save-dev typescript

Note: depending on what your project is, you might also want to install “@types” for other libraries you have dependency on, per instance, if you have a react-redux project, you might need to install the following:

npm install --save-dev @types/node
npm install --save-dev @types/react
npm install --save-dev @types/react-dom
npm install --save-dev @types/react-redux
npm install --save-dev @types/react-router-dom

After that, we need to add a ‘tsconfig.json’ file at the root directory of the project. That file contains compiler options needed to convert TS to JS. In order to have the least issues, use the following configuration to make JS code compatible with TS:

{
 "compilerOptions": {
     "module": "commonjs",
     "sourceMap": true,
     "jsx": "react"
 },
 "exclude": [
     "node_modules"
 ]
}

Note: You might need to change some bits based on your project. More on that here.

Now, add the following script in your package.json:

"tsc:w": "tsc -w"

And run it. It will run a watcher that transpiles all .ts (or .tsx) files into regular .js files. Also, it will generate these files at the same path that the original, therefore all imports and all build processes you might have, will still work as before, since the .ts files are completely ignored, and the result of the transpilation is used instead. The generated file structure has the following structure:

file.ts
 ├── file.js
 └── file.js.map

Now, all we need to do is to create our first ‘.ts’ file, by changing the extension of an existing one that we want to migrate to TypeScript, or create a blank file where to start working in our application.

Nevertheless, this change does not bring much change. We still can put normal JS code and get no help from what TypeScript has to offer. In order to TS to force us to actually type our code, we need to change the ‘tsconfig.json’ file. In particular, we will focus on two compiler options that will force our code to be actually typed:

"noImplicitAny": true,
"strictNullChecks": true,

Let’s imagine we have a simple mortgage simulator that tells the user if, given his financial situation, a mortgage is viable or not. For that, we will have a function that will retrieve somehow the savings user has:

function getSavings() {
 //returns savings
 }

And a function to decide if a mortgage is feasible:

function concedeMortgage(homeValue) {
     const savings = getSavings();
     return savings / homeValue > 0.2;
}

But, to make it actually work, we would need to check if the input exists. And also if it’s a number or not. The same applies for the return value from `getSavings`, since he don’t know what is the return type of that function. Therefore, our code could end up looking something like this:

function concedeMortgage(homeValue) {
     if(!homeValue || !parseFloat(homeValue)) return false;
     const savings = getSavings();
     if(!savings || !parseFloat(savings)) return false;
     return savings / homeValue > 0.2;
}

Which looks quite terrible.

But, if we activate the `noImplicitAny` compiler option, it would be no longer necessary to check if the input value and the return from `getSavings` are of type of number, so the function could look something like this:

function concedeMortgage(homeValue: number): boolean {
     if(!homeValue) return false;
     const savings = getSavings();
     if(!savings) return false;
     return savings / homeValue > 0.2;
}

Which is an improvement, since the compiler would help us to avoid some mistakes and typos, not allowing us calling the function with a string, per example:

concedeMortgage("foo")  // ERROR! Argument of type 'foo' is not assignable to parameter type 'number'

Unfortunately, null and undefined are still in the domain of every type, therefore it would be possible to do this:

concedeMortgage(null) // Still works

To fix that, we need activate the other option in the tsconfig.json file we mentioned: ‘strictNullChecks’.

Now, doing the call to the function with null, would end up caught by the compiler:

concedeMortgage(null) // ERROR! Argument of type 'null' is not assignable to parameter of type 'number'

That meaning, the check of null values is not needed by code, simplifying the logic to something like:

function concedeMortgage(homeValue: number): boolean {
     const savings = getSavings();
     return savings / homeValue > 0.2;
}

This is just a small glance at what TypeScript can give you if you migrate your project from plain JS to it.

If you would like to know more about how to migrate from JavaScript to TypeScript , I highly recommend you to subscribe to our monthly newsletter by clicking here.