Gergely Nemeth Co-Founder of RisingStack, EM at Uber

This tutorial teaches how you can build, structure, test and debug a Node.js application written in TypeScript. To do so, we use an example project which you can access anytime later.

Managing large-scale JavaScript projects can be challenging, as you need to guarantee that the pieces fit together. You can use unit tests, types (which JavaScript does not really have), or the two in combination to solve this issue.

This is where TypeScript comes into the picture. TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

In this article you will learn:

what TypeScript is,

what are the benefits of using Typescript,

how you can set up a project to start developing using it: how to add linters, how to write tests, how to debug applications written in TypeScript



This article won't go into to details of using the TypeScript language itself, it focuses on how you can build Node.js applications using it. If you are looking for an in-depth TypeScript tutorial, I recommend checking out the TypeScript Gitbook.

The benefits of using TypeScript

As we already discussed, TypeScript is a superset of Javascript. It gives you the following benefits:

optional static typing, with emphasis on optional (it makes porting JavaScript application to TypeScript easy),

as a developer, you can start using ECMAScript features that are not supported by the current V8 engine by using build targets,

use of interfaces,

great tooling with instruments like IntelliSense.

Getting started with TypeScript & Node

TypeScript is a static type checker for JavaScript. This means that it will check for issues in your codebase using the information available on different types. Example: a String will have a toLowerCase() method, but not a parseInt() method. Of course, the type system of TypeScript can be extended with your own type definitions.

As TypeScript is a superset of JavaScript, you can start using it by literally just renaming your .js files to .ts , so you can introduce TypeScript gradually to your teams.

Note: TypeScript won't do anything in runtime, it works only during compilation time. You will run pure JavaScript files.

To get started with TypeScript, grab it from npm:

$ npm install -g typescript

Let's write our first TypeScript file! It will simply greet the person it gets as a parameter:

// greeter.ts function greeter(person: string) { return `Hello ${person}!` } const name = 'Node Hero' console.log(greeter(name))

One thing you could already notice is the string type annotation which tells the TypeScript compiler that the greeter function is expecting a string as its parameter.

Let's try to compile it!

tsc greeter.ts

First, let's take a look at the compiled output! Ss you can see, there was no major change, only that the type annotations were removed:

function greeter(person) { return "Hello " + person + "!"; } var userName = 'Node Hero'; console.log(greeter(userName));

What would happen if you'd change the userName to a Number ? As you could guess, you will get a compilation error:

greeter.ts(10,21): error TS2345: Argument of type '3' is not assignable to parameter of type 'string'.

Tutorial: Building a Node.js app with TypeScript

1. Set up your development environment

To build applications using TypeScript, make sure you have Node.js installed on your system. This article will use Node.js 8.

We recommend installing Node.js using nvm, the Node.js version manager. With this utility application, you can have multiple Node.js versions installed on your system, and switching between them is only a command away.

# install nvm curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash # install node 8 nvm install 8 # to make node 8 the default nvm alias default 8

Once you have Node.js 8 installed, you should create a directory where your project will live. After that, create your package.json file using:

npm init

2. Create the project structure

When using TypeScript, it is recommended to put all your files under an src folder.

At the end of this tutorial, we will end up with the following project structure:

Let's start by adding the App.ts file - this will be the file where your web server logic will be implemented, using express.

In this file, we are creating a class called App , which will encapsulate our web server. It has a private method called mountRoutes , which mounts the routes served by the server. The express instance is reachable through the public express property.

import * as express from 'express' class App { public express constructor () { this.express = express() this.mountRoutes() } private mountRoutes (): void { const router = express.Router() router.get('/', (req, res) => { res.json({ message: 'Hello World!' }) }) this.express.use('/', router) } } export default new App().express

We are also creating an index.ts file, so the web server can be fired up:

import app from './App' const port = process.env.PORT || 3000 app.listen(port, (err) => { if (err) { return console.log(err) } return console.log(`server is listening on ${port}`) })

With this - at least in theory - we have a functioning server. To actually make it work, we have to compile our TypeScript code to JavaScript.

For more information on how to structure your project, read our Node.js project structuring article.

3. Configuring TypeScript

You can pass options to the TypeScript compiler by either by using the CLI, or a special file called tsconfig.json . As we would like to use the same settings for different tasks, we will go with the tsconfig.json file.

By using this configuration file, we are telling TypeScript things like the build target (can be ES5, ES6, and ES7 at the time of this writing), what module system to expect, where to put the build JavaScript files, or whether it should create source-maps as well.

{ "compilerOptions": { "target": "es6", "module": "commonjs", "outDir": "dist", "sourceMap": true }, "files": [ "./node_modules/@types/mocha/index.d.ts", "./node_modules/@types/node/index.d.ts" ], "include": [ "src/**/*.ts" ], "exclude": [ "node_modules" ] }

Once you added this TypeScript configuration file, you can build your application using the tsc command.

If you do not want to install TypeScript globally, just add it to the dependency of your project, and create an npm script for it: "tsc": "tsc" .

This will work, as npm scripts will look for the binary in the ./node_modules/.bin folder, and add it to the PATH when running scripts. Then you can access tsc using npm run tsc . Then, you can pass options to tsc using this syntax: npm run tsc -- --all (this will list all the available options for TypeScript).

Are you looking for help with enterprise-grade Node.js Development? Hire the Node.js experts of RisingStack!

4. Add ESLint

As with most projects, you want to have linters to check for style issues in your code. TypeScript is no exception.

To use ESLint with TypeScript, you have to add an extra package, a parser, so ESLint can understand Typescript as well: typescript-eslint-parser . Once you installed it, you have to set it as the parser for ESLint:

# .eslintrc.yaml --- extends: airbnb-base env: node: true mocha: true es6: true parser: typescript-eslint-parser parserOptions: sourceType: module ecmaFeatures: modules: true

Once you run eslint src --ext ts , you will get the same errors and warnings for your TypeScript files that you are used to:

5. Testing your application

Testing your TypeScript-based applications is essentially the same as you would do it with any other Node.js applications.

The only gotcha is that you have to compile your application before actually running the tests on them. Achieving it is very straightforward, you can simply do it with: tsc && mocha dist/**/*.spec.js .

For more on testing, check out our Node.js testing tutorial.

6. Build a Docker image

Once you have your application ready, most probably you want to deploy it as a Docker image. The only extra steps you need to take are:

build the application (compile from TypeScript to JavaScript),

start the Node.js application from the built source.

FROM risingstack/alpine:3.4-v6.9.4-4.2.0 ENV PORT 3001 EXPOSE 3001 COPY package.json package.json RUN npm install COPY . . RUN npm run build CMD ["node", "dist/"]

7. Debug using source-maps

As we enabled generating source-maps, we can use them to find bugs in our application. To start looking for issues, start your Node.js process the following way:

node --inspect dist/

This will output something like the following:

To start debugging, open the following URL in Chrome: chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/23cd0c34-3281-49d9-81c8-8bc3e0bc353a server is listening on 3000

To actually start the debugging process, open up your Google Chrome browser and browse to chrome://inspect . A remote target should already be there, just click inspect. This will bring up the Chrome DevTools.

Here, you will instantly see the original source, and you can start putting breakpoints, watchers on the TypeScript source code.

The source-map support only works with Node.js 8 and higher.

The Complete Node.js TypeScript Tutorial

You can find the complete Node.js TypeScript starter application on GitHub.

Let us know in the issues, or here in the comments what would you change!