Debugging TypeScript: How to Enable Source Maps in Node.js

Debugging TypeScript code can sometimes feel like a nightmare. Have you ever stared at a cryptic error message pointing to a specific line in a file, only to realize it’s pointing to your compiled JavaScript, not your actual TypeScript source code?

If you’ve ever felt that frustration, you are not alone. That’s where Source Maps come in.

In this guide, I’ll show you exactly how to enable source maps in a Node.js environment. By the end of this post, you’ll be able to transform your stack traces from cryptic compiled nonsense into clear references to your original code.


What Are Source Maps?

Source maps are files (usually ending in .js.map) that bridge the gap between your compiled JavaScript code and your original TypeScript source code.

When you write TypeScript, it gets compiled (transpiled) into JavaScript so the browser or Node.js can execute it. Without source maps, the runtime environment only knows about the JavaScript. Source maps tell the runtime: “Hey, line 50 in server.js actually corresponds to line 12 in server.ts.”

🚀 Complete JavaScript Guide (Beginner + Advanced)

🚀 NodeJS – The Complete Guide (MVC, REST APIs, GraphQL, Deno)


The Problem: Debugging Compiled Code

Let’s look at a typical scenario. I have a Node.js Express project using TypeScript. I’m using tsc and tsc-watch to compile my files.

In my server.ts file, I have a basic health check route where I’ve intentionally thrown an error:

TypeScript

// server.ts
app.get('/health', (req, res) => {
  throw new Error("Oops"); // triggering an error
});

When I run this using npm run dev and hit the endpoint, the console logs an error stack. The problem? The error points to server.js.

For a small project, you might be able to guess where the error is. But in a large codebase, manually mapping the compiled JS line back to the TS file is tricky and inefficient.

Let’s fix this using two different methods.


Method 1: The tsconfig Approach (Static)

The most common way to enable source maps is through your TypeScript configuration file.

Step 1: Update tsconfig.json

Open your tsconfig.json file and add the following line to your compilerOptions:

JSON

{
  "compilerOptions": {
    "sourceMap": true,
    // ... other options
  }
}

Step 2: The Critical Runtime Flag

This is the step most developers miss. Generating the map files isn’t enough; you have to tell Node.js to use them.

Go to your package.json and look at your script for running the app. You need to add the --enable-source-maps flag to the node command.

JSON

"scripts": {
  "dev": "tsc-watch --onSuccess \"node --enable-source-maps dist/server.js\""
}

Now, when you restart your server and trigger that error, the stack trace will reference server.ts specifically. You can see exactly where the error occurred in your original code.

If you look in your dist folder, you will now see .js.map files sitting right next to your .js files.


Method 2: The CLI Approach (Flexible)

While Method 1 works, it hardcodes the source map generation into your project. If you want to turn source maps off for production builds to save space, you can’t do it easily without changing the config file.

I recommend a more flexible approach using CLI flags.

Step 1: Clean up tsconfig

First, remove "sourceMap": true from your tsconfig.json.

Step 2: Update package.json

Instead of putting the config in the file, we will pass it as a flag to the TypeScript compiler (tsc or tsc-watch) inside package.json.

JSON

"scripts": {
  "dev": "tsc-watch --sourceMap --onSuccess \"node --enable-source-maps dist/server.js\""
}
  • --sourceMap: Tells the compiler to generate the .map files.
  • --enable-source-maps: Tells Node.js to read those files for stack traces.

Why is this better?

This approach allows you to decide at build time whether you want source maps.

  • In Development: You run the command above, giving you full debugging power.
  • In Production: You can choose.
    • If you want a smaller codebase, remove the flags.
    • If you want the ability to trace production errors (the “necessary evil” of larger file sizes), keep the flags in your build script.

A Note on Production

One could argue that .map files bloat the source code in production. However, errors do happen in production. The ability to efficiently trace a bug to a specific line of TypeScript code often outweighs the cost of the extra file size.

By using the CLI approach (Method 2), you have the power to choose what works best for your specific deployment needs.


Conclusion

Turning on source maps in a TypeScript project is simple, but it can save you a ton of time down the road. Whether you configure it via tsconfig.json or use the flexible CLI flags, just remember that you must enable source maps in Node (--enable-source-maps) to see the magic happen in your console.

Happy debugging!

Share this article

Similar Posts