This post was updated on 8 August 2023 to use the latest LTS version of Node and TypeScript 5.
In this tutorial, you will learn how to add TypeScript support to Node.js projects. We will address common needs, such as:
- compiling and executing the code
- debugging the source files
- configuring third-party packages so that the TypeScript compiler also validates them
Let's get going!
Why Use TypeScript?
TypeScript brings optional static typing to JavaScript projects. The primary benefit of static typing is that type errors are detected and corrected at build time, so code will more likely run correctly once deployed to production. A growing number of JavaScript developers are seeing the value of writing more strongly typed code, which has led to an increase in the adoption of TypeScript for all kinds of JavaScript-based projects.
Prerequisites
This article assumes that you have a basic knowledge of Node.js and TypeScript. You also need to have a recent version of Node.js and npm installed on your computer.
Installing and Configuring TypeScript on Node.js
Besides the web browser, Node.js is the most popular platform on which JavaScript code is executed. However, just like the browser, it lacks native support for TypeScript code (unlike Deno, for example).
Before installing TypeScript, make sure that you've created and initialized a
Node.js project with a package.json
file. TypeScript is available as a package
on the npm registry, and it can be
downloaded into your project through a package manager like
npm or yarn:
Once the above command succeeds, you can check the current version through the following command:
It's advisable to install TypeScript as a project-specific dependency. The same version of the language will then be used, regardless of the machine running the
code. You can also install the TypeScript CLI
globally by using the --global
switch. A global installation can come in handy
for running one-off scripts for testing purposes.
If you use a Node.js environment manager like Volta, you'll be able to switch between globally installed TypeScript versions and project-specific ones seamlessly.
Now that TypeScript is installed in your project, create a
configuration file that specifies
which files should be compiled and the compiler options for the project.
This file is called tsconfig.json
, and you should place it at the root of your
project directory.
Here's a basic configuration that you can start with:
The above configuration file extends the base
configuration provided by the TypeScript
team for the latest LTS version of Node.js (currently v18). Additional
options or overrides
may be included through the compilerOptions
property. It also specifies that all
the files in the src
directory should be included in the program, but
everything in the node_modules
directory is skipped entirely. Both the
include
and exclude
properties support glob
patterns.
Before you proceed, ensure you add the base configuration package for Node.js LTS
to your project's devDependencies
through the command below. Base
tsconfig.json
packages also exist for Node
10, Node
12, Node
14, up to Node 20, at the time of writing.
Compiling TypeScript Files for Node.js
Go ahead and create the aforementioned src
directory in your project root, and
place a main.ts
file inside it. This file should contain the following code:
Save the file, then attempt to compile the TypeScript code to JavaScript through the command below:
You will get an error indicating that the compiler does not understand the
console
object:
This error occurs because the lib
compiler option set in the base
configuration for Node.js LTS
does not include the dom
option, which contains type definitions for the
console
object and other browser-specific APIs. The error message above
suggests adding the dom
option to the lib
property to fix the
problem, but this is not the correct solution for a Node.js project.
The correct fix involves installing the type definitions for Node.js APIs so that the TypeScript compiler can understand and validate all the built-in Node.js APIs. Here's how:
Once installed, the error will vanish the next time npx tsc
is run and a
main.js
file will be produced in the src
folder with the following content:
You can subsequently execute this file through the node
command:
If you want to change the folder where the JavaScript files are placed, you can
use the outDir
compiler option in your tsconfig.json
file:
Subsequent compilations will emit the JavaScript output into a
dist
folder.
Execute TypeScript Source Files Directly With ts-node
The process of compiling TypeScript source files into JavaScript code before
executing them with Node.js can get a little tedious after a while, especially
during development. You can eliminate the intermediate steps before running the program through the ts-node CLI
to execute .ts
files directly. Go ahead and install the
ts-node
package using the command below:
Afterward, execute the main.ts
file with the ts-node
command:
Using ts-node
in this way places the TypeScript compiler as a
middleman between the source files and the Node.js runtime. It transpiles the
source code before executing the resulting JavaScript code with node
(performed under the hood). This makes the script
execution a bit slower than JavaScript output directly with
node
. You can opt out of type checking through the --transpile-only
or -T
flag to make the script execute faster in scenarios where type validation isn't
essential.
Another feature that ts-node
enables is the transformation of modern
import
syntax to CommonJS syntax. This means that when using ts-node
, you
can use import
instead of require
to utilize Node.js modules in your code.
Learn more about this feature in the ts-node
project's README
document.
TypeScript Integration with Third-party Node.js NPM Packages
When utilizing Node.js packages from the npm registry, additional setup may be required to compile the project successfully.
The main problem is that most
of the packages you're likely to encounter are written in vanilla JavaScript, so
TypeScript cannot determine the valid types for exposed methods. Everything from the library is implicitly typed as any
.
Here's an example that utilizes the popular Express package to create a web server:
Assuming you've installed the express module with npm install express
, execute
the script with ts-node
. It should yield the following errors:
The TypeScript compiler is responsible for the errors shown above. It cannot determine the type of the req
and res
parameters in the callback
function, so they are both implicitly typed as any
.
Since the strict
compiler option is set to true
in the base
tsconfig.json file, the
noImplicitAny compiler
option is also enabled. This ensures that TypeScript will emit an error instead of
inferring type any
when it is unable to determine the type of a value.
You can fix this error by providing a type declaration file for the express
module so that the TypeScript compiler can accurately determine the valid types for its exported methods. You can find up-to-date type definitions for
many popular npm packages in the DefinitelyTyped GitHub
repository.
The type
definition for a package can be downloaded through the @types
scope. For
example, use the command below to install the type definitions for
Express:
After installing the type definitions for Express, compiling and executing the
script with ts-node
should complete successfully without errors.
Linting TypeScript with ESLint
An important step towards adding comprehensive support for TypeScript in a Node.js application is setting an adequate linting workflow. You can make use of the popular ESLint package to lint TypeScript code. Although it was originally written for JavaScript code, it also supports TypeScript with the help of a few plugins. Go ahead and install the eslint package in your project with the command below:
Now create a new .eslintrc.js
file in the root of your project
directory. Here is where you'll place the configuration
settings
for ESLint. Note that with the release of ESLint v9.0.0 the config system will change and the config file will be named eslint.config.js.
To add TypeScript support to ESLint, install the @typescript-eslint/parser and @typescript-eslint/eslint-plugin packages in your project. The former is used to parse TypeScript code to a format that is understandable by ESLint, while the latter provides TypeScript-specific linting rules.
Once both packages are installed, open up your .eslintrc.js
file in your
editor, and enter the following:
If you want to override any of the linting
rules or configure other rules, use the rules
property in the .eslintrc.js
file, as shown below:
After saving the file, run the ESLint CLI on your TypeScript code through this command:
You can also create a lint
script in your package.json
file as follows:
Then run ESLint:
To prevent ESLint from linting certain files or directories, create a
.eslintignore
file in your project root, and place the patterns for files
to ignore therein. Here's an example in which all generated files in the dist
folder are ignored:
You can also decide to omit every single JavaScript file in your project directory with the following pattern:
Note that everything in the node_modules
folder, and files or folders that
begin with a dot character (except eslint config files), are ignored automatically,
so there's no need to place patterns matching such files in your .eslintignore
file.
Debug Your TypeScript Code with Visual Studio Code
Debugging TypeScript source files is easy and straightforward with the help of
ts-node
and Visual Studio Code. All you need to do is create a launch
configuration file (launch.json
) within the .vscode
directory in the project
root and populate the file with the following code:
The ts-node/register
method is preloaded in the above file to handle
TypeScript source files correctly. Secondly, the name of the TypeScript file to
run when starting a debugging session is provided as the first value in the
args
property.
Go ahead and start debugging your Node.js project by pressing F5 on your keyboard. Try to set a breakpoint, then inspect the values in the current scope once the breakpoint is hit. It should work as expected, without issues!
Deploying TypeScript to Production
According to its author, ts-node is safe to use in
production. Nonetheless, to reduce the start-up time of the server
and prevent additional memory usage from keeping the compiler in
memory, it's better to compile the source files beforehand. Execute the
resulting JavaScript code with the node
command when deploying to production.
Node.js and TypeScript: Summing Up
In this article, you learned how to configure a Node.js project with TypeScript support and run TypeScript source files directly, without an intermediate compilation step.
We also covered how type definition files work, and how to take advantage of predefined type definition files for popular npm packages so that the TypeScript compiler fully validates all third-party dependencies.
Finally, we discussed how to debug TypeScript files in VSCode, and what to do when deploying your TypeScript project to production.
Thanks for reading, and happy coding!
P.S. If you liked this post, subscribe to our JavaScript Sorcery list for a monthly deep dive into more magical JavaScript tips and tricks.
P.P.S. If you need an APM for your Node.js app, go and check out the AppSignal APM for Node.js.