My Many Colored Ways

A personal site, by Kyle Halleman.

Using TypeScript in Node, the simple way

It’s 2021. You’re tired of seeing undefined is not a function. It might be time to try using TypeScript. It’s basically JavaScript with types, plus all the new hotness in ECMAScript land. Types are wonderful. They help you write safer code with fewer errors.

Yes, there are more strongly typed languages out there. Including ones that compile to JavaScript (Reason, PureScript, etc.). But of the typed compile-to-JavaScript languages, TypeScript is a great first step into typed languages.

TypeScript has a large community so you know you won’t be alone. Whatever issue you run into, someone else has had it and already asked about it on StackOverflow. It has a great ecosystem built around it. Visual Studio Code works wonderfully with TypeScript. Most open-source projects in JavaScript now either come with their own TypeScript type definitions, or have third-party ones that can be found on DefinitelyTyped. You even have multiple options for using it in Node.

Which is why we’re here.

Deno

If I were starting a personal project from scratch today, I would try to use Deno. It’s a secure runtime for TypeScript by the creator of Node. The problem is it is less than a year old so it’s still evolving and the ecosystem isn’t quite there yet (or maybe it is—I don’t know I haven’t had a chance to find out. Sounds like a good topic for a future post). If you work at a large company like I do, you are not going to be using it any time soon.

But that’s okay. You have other options if you want to use TypeScript where previously you’d have used Node.

You’re probably already using Babel…

How you set up TypeScript will depend on whether you also have a front end that will also be using TypeScript. You’ll want to use the same tooling for server and client. In that case, you’re probably going to want to use Babel, because you’re probably already using Babel. The good news is that setting it up is pretty easy. And not something I’m going to cover here. At least not today.

…But we’re not going to use Babel

If you are writing a pure server app, say a microservice for that large company you work at, then you probably are not currently compiling your Node code. So you’re not already using Babel. And you don’t need Babel, so don’t start adding it. Instead, we’ll be using TypeScript’s tsc compiler to translate your TypeScript into JavaScript.

That’s basically it. But you probably want to make development easier. And hopefully you’re writing tests. So there’s a few more tools we need.

Getting started

Let’s start with installing TypeScript, npm install -D typescript. With TypeScript comes its command-line compiler, available via tsc. Add a build script to your package.json.

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

There. You have your build step.

Configuration

We’ll add some configuration options in a tsconfig.json that will be at the root of your project. You can generate a starter config by running npm run build -- --init. This creates a tsconfig.json in your root with a lot of options, most of them commented out. This will help you see what is available and what defaults are. A few newer options aren’t present for some reason, like resolveJsonModule. But if you’re using Visual Studio Code (a good idea for TypeScript development) you’ll get Intellisense support for these other options.

At the top of your tsconfig.json, outside of compilerOptions, I recommend extending from a base config. In the sample project I’ve created to go along with this post, I’m using Node 14, so this is the file I’m extending from. Notice all of the options tsc --init didn’t comment out are handled by this base config, so we can safely remove them. 1

One option to uncomment is outDir. Set this to wherever you want your compiled JavaScript to be output. I chose .build but you do you. Since you’ll be running compiled code, you should probably set sourceMap to true. This will save you headaches when debugging.

To make your life easier, I’d recommend putting your app’s code in a src directory. In your tsconfig.json, add this for the include option:

{
"include": ["./src"]
}

This will save you from having to list a bunch of files and folders in the exclude option, because exclude is a list of files to be skipped when resolving what’s in include. So, if you have your project code just strewn about everywhere in root, you’re going to have to add a lot of lines to exclude, because the default for include is ["**/*"].

A few options for existing projects

If you’re adding TypeScript incrementally to an existing codebase, you’ll want to set allowJs to true and set strictNullChecks and noImplicitAny to false. Once you’ve got everything in TypeScript, flip those options around. But first you need to ease into adopting TypeScript. Like an old man into a nice warm bath.

Development

Isn’t hot reloading fantastic when developing a React app? How about that, but for TypeScript and Node? Enter ts-node-dev, a love child between ts-node and node-dev. Unlike nodemon, which watches the file system for changes, ts-node-dev hooks into Node’s module system to only restart the Node process when a file that is actually used is changed. 2

Yeah, but it’s probably a pain in the ass to get this all set up, right?

npm install -D ts-node-dev
// package.json
{
"dev": "ts-node-dev --files src/index.ts"
}

Note the --files flag, which tells ts-node to use your config’s files, includes, and excludes. This allows your custom type definition files to work. Otherwise you might be banging your head against the desk wondering why tsc builds but ts-node-dev is throwing compilation errors (true story).

Testing

Jest is great. You’ve probably used it before. Good news! There’s ts-jest, which makes it a breeze to run Jest tests in TypeScript. You even get to use ECMAScript modules (import/export) in Jest without a headache. That alone should be reason enough to use TypeScript.

npm install -D ts-jest

Then, add preset: "ts-jest" to your Jest config. You’ll run Jest as you normally would. Check out this very elaborate test I set up.

Build

Wait. I thought we already covered that?

Yes. Building your TypeScript app is that simple: tsc.

That wasn’t so bad

Getting started with TypeScript in Node is easier than I thought it would be at first. I was fearful of confusing Babel and Webpack configurations and plugins. But if you’re focusing solely on Node, a few zero or low-configuration tools will get you going. 3

Now that you’re all ready to use TypeScript in your app, you might be wondering just how to really take advantage of what the language offers.

Stay tuned.

  1. You can see the full tsconfig.json that accompanies this post on Github.↩︎

  2. Unfortunately it doesn’t reload the browser for you 😢.↩︎

  3. I created a bare-bones repo to go along with this post, showcasing the very small set of dependencies and configuration needed to get started.↩︎