LeanyLabs
Our Blog

How to Migrate Application From JavaScript to TypeScript

January 21, 2021 • 7 min read

Migrating an application from JavaScript to TypeScript can be challenging and time-consuming, but it’s never too late.

TypeScript is a superset of JavaScript, so valid JavaScript code is a valid TypeScript code. Incremental migration, converting the whole project step by step is much more comfortable. Start by adding TypeScript to your build pipeline. Then migrate each code file one-by-one.

There are some tools and tricks to help you with that.

JS to TS Converter

TypeScript code contains much more information than JavaScript. That’s the main reason why TypeScript exists. Perfect automatic conversion between the two is theoretically impossible, or else the tools could perform it on the fly, and you’ll have all the benefits of TypeScript in plan JavaScript.

However, a tool called ts-migrate will automatically convert the whole JavaScript project to a compiling TypeScript. It won’t generate perfect type-safe code, but it’s a good starting point that will significantly speed up the migration process. The will be // @ts-expect-error and any all over the place that you’ll have to fix manually over time. Yet, it’s much better than doing everything from scratch.

This tool will create the tsconfig.json for you with a reasonable default configuration. Then it will change file extensions for all the source code files from .js/.jsx to .ts/.tsx (yes, it supports JSX syntax too). It will already save you from spending hours doing monotonous work.

Ts-migrate has plugin architecture with multiple code modification scripts - codemods. They have access to code information via the TypeScript language server and can transform the code. Each step creates a separate git commit so you can easily track changes made by the tool.

Successful project compilation with the same runtime behavior is a top priority. To ensure that the resulting TypeScript project compiles, it has tsIgnorePlugin to detect all compilation errors and fix them by adding // @ts-ignore comments with meaningful information about each error.

Just install the tool:

yarn add --dev ts-migrate

Then run the migration on the whole project with:

npx -p ts-migrate -c "ts-migrate-full <folder>"

That’s it! Your project should be in compilable TypeScript now. You can tweak the resulting tsconfig.json configuration and slowly fix all the // @ts-ignore and any file by file or type by type.

The Airbnb team that created the tool managed to migrate projects with more than 50,000 code lines across 1,000+ files in a single day! Feel free to read more on their engineering blog.

Manual Incremental Migration

You might want to migrate slowly, file by file, controlling every step. This process is much more tedious but also possible.

Start by adding typescript to your project:

yarn add -D typescript

Next, you’ll need to add tsconfig.json with TypeScript compiler configuration. You can do it manually, but it’s much faster to generate the file with default options turned on, and a list of possible options commented out:

npx tsc --init

You should change the outDir to your output files’ desired directory, point include to your source files and make other changes as needed. Don’t forget to set allowJs to true! It will tell the compiler to accept JavaScript files as inputs.

You might need to install TypeScript definition files for your project dependencies. Some packages already have them, but you’ll have to install the corresponding @types/<library-name> package for the rest. Those are dev dependencies:

yarn add -D @types/node

yarn add -D @types/react @types/react-dom @types/react-redux @types/react-router-dom

At this stage, your project should compile with tsc, but it’s still JavaScript.

Adding TypeScript to Your Build Pipeline

Every project has different tools in the build pipeline. Sometimes it makes sense to leave JavaScript in the build configuration files to keep things a lot simpler. There aren’t many of those files, and they aren’t long anyway.

If you’re still using Gulp, there’s an official tutorial for adding TypeScript support that also covers integration with Browserify, uglify, Watchify and Babelify.

Adding TypeScript to Webpack is pretty straightforward. You’ll need ts-loader to handle source files and source-map-loader for easy debugging:

yarn add -D ts-loader source-map-loader

Now add the following options to your webpack.config.js:

module.exports = {
  entry: "./src/index.ts",
  output: {
    filename: "./dist/bundle.js",
  },
  // Enable sourcemaps for debugging webpack's output.
  devtool: "source-map",
  resolve: {
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"],
  },
  module: {
    rules: [
      // All files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'.
      { test: /\.tsx?$/, loader: "ts-loader" },
      // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
      { test: /\.js$/, loader: "source-map-loader" },
    ],
  },
  // Other options...
};

Note that ts-loader should run before any other loader that handles .js files.

Migrating Code Files

Before you start converting code from JavaScript to TypeScript, make sure to configure the compiler to enforce typed code by changing the following options in ts.config.json:

{  
  "noEmitOnError": true,
  "noImplicitAny": true,
  "strictNullChecks": true,
}

The first option will stop the compiler from outputting any JavaScript in case of errors, so you’ll have to fix them. noImplicitAny will forbid to silently infer any type if the compiler cannot automatically detect the type. With strictNullChecks you’ll have to manually consider where values can become null and undefined as they won’t be a subset of every type.

Now you can start renaming .js/.jsx files to .ts/.tsx and fixing all the compilation errors. Adding a script to package.json that will run the compiler in watch mode might help:

{
  "tcs:w": "tsc -w"
}

You can also add specific scripts that will run the whole project in watch mode. For example, for Node.js applications, you can use ts-node and the similar nodemon.json:

{
  "watch": ["src"],
  "ext": "ts,json",
  "ignore": ["src/**/*.spec.ts"],
  "exec": "ts-node ./src/index.ts"   
}

Fixing the files isn’t easy. They have dependencies on other files in the application, and it’s hard to do all at once. Adding temporary :any and // @ts-ignore and converting the project type by type might be a good option. Don’t forget to add comments so that you can find and go back to all those places in the future.

Once you finished migrating all the files, don’t forget to turn off allowJs in the tsconfig.json. You can also turn on all the strict mode checks using a single strict flag.

Conclusion

TypeScript provides many advantages over JavaScript. You’ll avoid many bugs and may even find some hidden ones during the process. Developers will be happy, as TypeScript is the second most loved programming language in the world. Compile-time checking, code suggestions, working refactoring tools, and improved code navigation are well worth it.

Migrating the project is never too late, and with tools like ts-migrate it has become much more straightforward. You may need some manual tweaks, and the code won’t become perfectly typed automatically. Yet, it’s a good start that requires minimal effort. It will definitely payout in the long run.

LeanyLabs has a lot of experience with both JavaScript and TypeScript. We’ll be glad to help you seamlessly migrate your project. Just contact us using the form below.

Contact Us