Migrating to TypeScript, Part 1: Introduction and getting started

Migrating to TypeScript, Part 1: Introduction and getting started

In this first article of a multi-part series on migrating to TypeScript, we look into the things you need to do to prepare your project for the Big Rewrite.

This post is also published on:

Header image by Franz Harvin Aceituna on Unsplash.

TypeScript (TS) is a language which has seen quite a meteoric rise lately. It's gone some favourable results on the 2018 State of JavaScript (JS) survey. It has even come to the point where big names like Kent C. Dodds started migrating into it.

To learn more about how the TypeScript type system works, and how it can help you, watch this talk by Anders Hejlsberg, the creator of TypeScript.

For many of us already using TypeScript, we could never imagine writing JS without it anymore. And with newly-added support for Babel compilation, it gets much easier to integrate with the rest of the JS ecosystem. But for many people looking to migrate their apps into it, it could feel a little too overwhelming. This gets further out of control when you're looking at a medium/large-sized app, all already written in JavaScript.

A lot of TypeScript learning materials out there never seem to dive deep on migrating a well-matured app to TypeScript. Worse still, TypeScript does have their own, official migration guide - but it's horribly outdated.

So in this series of posts, I try to outline my personal steps on how to migrate an existing codebase to TypeScript. The first part will go through the steps on preparing your project for the Big Rewrite. This includes setting up the TS compiler, and the basic essentials of the TypeScript compiler.

Table of contents


So what is TypeScript, exactly?

TypeScript is a superset of JavaScript that compiles to plain JavaScript code. It enables great tooling and developer experience through the power of static typing. Some of the improved JS experience being unlocked by static typing includes better refactoring tools, statement completion, and more.

TypeScript was authored by Anders Hejlsberg, known for being the lead architect of C# and creator of Turbo Pascal. TypeScript 2.0 was released on September 2016, with much-improved Node.js modules support and stricter null checking. Since then, the language is continuously improved with features like object rest/spread, --strict mode, conditional types, and more. TypeScript 3.0, released in July 2018, even has support for monorepos through project references.

To read more about TypeScript, I recommend the TypeScript Deep Dive book by Basarat.

Getting started with TypeScript

So to start off, we will need to set up our environment for TypeScript. There are two ways to set this up:

  • You use Babel 7 + TypeScript preset to compile, and have the TypeScript compiler only do the type-checking.
  • You use the TypeScript compiler to both type-check and compile your code.

Since we’re migrating from JavaScript, we can assume that we’re already using Babel in our development toolchain, so we can go with the first option. You can also run the second option and chain with Babel. But the first option is still better if we want to have finer control over the Babel presets/plugins we use as well.

Initialising the compiler

This guide will make use of TypeScript 3.2. It should work as well on any versions starting from 3.0+.

To get started with TypeScript, install the TypeScript compiler CLI by running:

bash
$ npm install -g typescript

Then run tsc --init to initialise a tsconfig.json file with the default options. It lists out all the options available as well as an explanation, with the non-essential options commented out. The number of options may overwhelm you, but let's break the config down to just the essentials.

tsconfig.json

json
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"esModuleInterop": true,
"downlevelIteration": true,
"lib": ["esnext", "dom"],
"module": "commonjs",
"noUnusedLocals": true,
"outDir": "dist",
"skipLibCheck": true,
"strict": true,
"target": "esnext"
},
"include": ["src"]
}

This setup will take everything from the src and compile it into the dist folder. There are some other essential compiler options here, but we'll go through them in the next section. To compile, run the tsc command.

Note: If you use webpack to compile things, you don't need the outDir option!

Setting up build tasks

Now that the TypeScript compiler works, we can include it as a script in our package.json file!

json
{
"scripts": {
"build": "tsc"
}
}

This way, you can simply run yarn build (or npm run build if you're running npm) to build your project.

Wiring up the TypeScript Babel preset (Optional)

If you already use Babel to compile your ES6+ JS code, you can use the TS preset for Babel. Note that you need Babel 7 and above to use this.

json
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"],
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-object-rest-spread"]
}

The TypeScript compiler supports all modern ES2015 features, as well as next-generaton ES features. Though one common pitfall is that you can't use next-generation syntax newer than stage-3, since TS doesn't support it. This means that using proposed syntax like the pipeline will give you type errors. The proposal plugins should include the stage-3 features required for TypeScript transpilation.

Note that the Babel compiler only removes the types from your code. It does not do any extra type-checking! Make sure to run type-checking separately with tsc --noEmit. Or better yet, add it as a compiler option into your tsconfig.json:

json
{
"compilerOptions": {
"noEmit": true
}
}

This option will run the TypeScript compiler without outputting any code, so it only runs type-checking. You can then add the tsc command to your package.json scripts, which will help if you use a CI system as well.

json
{
"scripts": {
"type-check": "tsc"
}
}

Note: If you use Flowtype, you can't use the Flowtype Babel preset together with the TypeScript preset. You have to choose one or the other!

tsconfig.json essentials

The above tsconfig.json file already contains the essential compiler options when working with TypeScript. Let's go through the essentials one by one.

TS/JS interoperability

The TypeScript compiler can also be set up to type-check and compile JS files alongside TS files. allowJs allows regular JavaScript files to be compiled. If you want to also enable type-checking in JavaScript files, you can also enable checkJs. If you're just getting started, it's recommended to disable checkJs and manually enable per-file type checking. To do that, add a // @ts-check comment on the top of the JS file you'd like to type-check.

Another compiler option to take note of is esModuleInterop. This allows you to do default imports with CommonJS modules (e.g. import React from 'react';). For TS veterans, this option is similar to allowSyntheticDefaultImports. The only difference is that it added some helpers during compile time for improved Babel interoperability.

Libraries and compile targets

There are three options that define how your TS code is interpreted by the compiler.

lib outlines the TS library files used for compilation. Some libraries that are commonly used are:

  • esnext - Modern ESnext features (up to stage-3 recommendations)
  • es201x - Yearly ES specifications. Note than including one year will include all of the yearly specs before it (e.g. es2018 will also include es2017, es2016 and es2015).
  • dom - DOM-specific APIs.
  • webworker - APIs for Web workers.

target defines the target version of ES.

module defines the module type the TS compiler will generate. If you set target to es5 or below, it will default to commonjs (standard CommonJS modules for Node.js compatibility). Otherwise, it will default to esnext (ES Modules).


And that's it for this part. In part 2, we'll go through how to make your TypeScript migration painless by adding types gradually. We'll also go through the quirks of TypeScript's type system, as well as changing your way of thinking to write TypeScript apps.

Once again, I really recommend the TypeScript Deep Dive book by Basarat. His book on TypeScript has helped me a lot on learning this language. Should you ever get stuck, the #typescript channel on the Reactiflux Discord server has a bunch of lovely people who know TypeScript inside and out. Feel free to hop in and ask questions!

Resi Respati

Web developer based in Jakarta, Indonesia.