DEV Community

Cover image for Migrating to TypeScript, Part 1: Introduction and getting started
Resi Respati
Resi Respati

Posted on • Originally published at resir014.xyz on

Migrating to TypeScript, Part 1: Introduction and getting started

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.


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:

$ npm install -g typescript
Enter fullscreen mode Exit fullscreen mode

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

{
  "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"]
}
Enter fullscreen mode Exit fullscreen mode

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!

{
  "scripts": {
    "build": "tsc"
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

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

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:

{
  "compilerOptions": {
    "noEmit": true
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

{
  "scripts": {
    "type-check": "tsc"
  }
}
Enter fullscreen mode Exit fullscreen mode

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 amazing 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!

Top comments (4)

Collapse
 
simonholm15 profile image
Simon Holm

typescript is awesome!

I recommend:
ts-node (if you want to run node and write typescript) github.com/TypeStrong/ts-node

Create React App with --typescript (if you want to give React with typescript a try) facebook.github.io/create-react-ap...

Typescript can scare you at first with errors and warnings all over the show! You can tweak and adjust almost everything until its to your liking. It's worth the effort.

edit and tweak:
tsconfig.json
tslint.json

If you code in VSCODE check out the plugins
marketplace.visualstudio.com/items...
marketplace.visualstudio.com/items...

Collapse
 
vignesh0025 profile image
Vignesh D

I was trying to use Typescript with react and Evergreen-Ui react library. But Evergreen-ui doesn't have typescript support. It's asking for declaration file which I couldn't understand. How to proceed with it?

Collapse
 
resir014 profile image
Resi Respati • Edited

I was planning to talk about dealing with libraries without TypeScript declarations in the next couple parts, but for a quick refresher:

  • Create a types/ folder inside your root src/ folder
  • Create a file to hold our own custom declarations for Evergreen UI, e.g. evergreen-ui.d.ts

Now inside the .d.ts file we just created, put the following:

declare module 'evergreen-ui';

This will shim the evergreen-ui module so we can import it safely without the "Cannot find module" errors.

Note that this doesn't give you the autocompletion support, as you will have to declare each components you use inside it. This next part is optional, but very useful if you want better autocompletion.

For example, if we were to use Evergreen UI's Button component:

// Import React's base types for us to use.
import * as React from 'react';

declare module 'evergreen-ui' {
  export interface ButtonProps extends DimensionProps, SpacingProps, PositionProps, LayoutProps {
    // The above extended props props are examples for extending common props and are not included in this example for brevity.

    intent: 'none' | 'success' | 'warning' | 'danger';
    appearance: 'default' | 'minimal' | 'primary';
    isLoading?: boolean;

    // Again, skipping the rest of the props for brevity, but you get the idea.
  }

  export class Button extends React.PureComponent<ButtonProps> {}
}

Again, more on this on the next few parts on this series, so watch this space!

Collapse
 
vignesh0025 profile image
Vignesh D

Thank you Bro. It did the work as a starting point to add other declarations.
It would be better if there is a tool that autogenrates declaration files using reflection anyway.