How to Migrate Application From JavaScript to TypeScript
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.