Page Content

Tutorials

How to Debug TypeScript code in VS Code?

Debug TypeScript

Debugging in the context of TypeScript (TS) involves the processes used to investigate, locate, and resolve flaws in the codebase that cause unexpected behaviour during execution. Although TypeScript is designed to drastically reduce the number of bugs that make it to production, debugging remains necessary because runtime exceptions, such as network failures or errors parsing user input, can still occur regardless of the language used.

Why TypeScript Debugging is Different

The core motivation for leveraging TypeScript is to minimise the need for extensive runtime debugging by catching errors early.

In traditional JavaScript (JS), most issues are only discovered when the program is actually run, leading to errors surfacing at runtime. Since JS is an interpreted language, developers might spend hours trying to find bugs because issues are not highlighted until the execution phase. JavaScript’s flexible, dynamic nature and tendency to avoid exceptions (often returning values like NaN or undefined) delay the discovery of mistakes, creating a painful debugging experience. This inherent unreliability makes debugging large JavaScript codebases particularly difficult and challenging to scale.

TypeScript addresses this fundamental issue by introducing static typing and compile-time error checking. TypeScript’s type checker validates that the code is typesafe before execution. This process eliminates entire classes of type-related bugs and prevents runtime errors, such as forgetting a null check or misusing a variable before it has been assigned a value. Furthermore, modern editors like Visual Studio Code leverage the TypeScript Language Service to provide immediate feedback, showing error messages (red squiggly lines) as the code is typed, which is significantly more pleasant than waiting for a runtime crash. This capability makes TypeScript code inherently easier to debug and maintain than standard JavaScript.

How TypeScript Code is Debugged

When TypeScript code is executing on a platform such as Node.js, there are two main methods for debugging it.

Debugging via Transpilation and Source Maps

The conventional approach entails converting the TS files into JavaScript, executing the generated JS code, and connecting runtime execution to the original TS using source maps.

Source Map Functionality

Descriptive files called source maps (.js.map files) associate the line and column numbers in the TypeScript file with the locations in the output JavaScript file. Because source maps enable tools (such as Visual Studio Code or Chrome DevTools) to resolve variables within the TS context and halt execution on breakpoints put in the original .ts file, even when the engine is running the compiled .js code, they are crucial for debugging.

Configuration for Source Maps

Setting the sourceMap compiler option to true in the tsconfig.json file is necessary to enable source map generation.

// tsconfig.json excerpt
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    // ... other options
    "sourceMap": true /* Generates corresponding '.map' file. */
  }
}

Once compiled (by running tsc), the debugger configuration (e.g., launch.json in VS Code) must point to the generated JavaScript entry file and explicitly set sourceMaps: true:

// launch.json configuration (for Visual Studio Code debugging)
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "program": "${workspaceRoot}\\index.js", // Points to compiled JS
            "cwd": "${workspaceRoot}",
            "outFiles": [],
            "sourceMaps": true // Enables mapping back to TS
        }
    ]
}

With this setup, the index.js file is executed by the Node process, but the debugger makes advantage of the source maps to let the developer work with the actual TypeScript code, allowing them to set breakpoints and examine variables in the TS environment.

Debugging by Running TS Directly

In the second approach, TypeScript files are executed directly without the need for a manual precompilation step using tsc thanks to the npm package ts-node. Live compilation and execution are done via the ts-node.

Running TS Files

Installing the ts-node, either locally or globally, is required. Presuming that it is deployed everywhere:

npm install -g ts-node 

To execute a TypeScript script named main.ts:

// main.ts
console.log("Hello world"); 

The command to run this file is straightforward:

ts-node main.ts 

Example Output:

$ ts-node main.ts
Hello world

For TypeScript experiments, ts-node can additionally offer a Read-Eval-Print Loop (REPL).

Debugging Setup

For debugging environments like Visual Studio Code, ts-node can be configured using runtime flags in a package.json script. This setup tells Node.js to enable the debugger (--inspect) and halt on the first line (--debug-brk), running the specified entry file (index.ts) through ts-node.

Sample bundle.json script:

// package.json script for ts-node debugging
{
  "scripts": {
    "start:debug": "ts-node --inspect=5858 --debug-brk --ignore false index.ts" 
  }
}

Because ts-node manages the compilation and source map process internally during execution, this method does not require the management of intermediate .js files or the configuration of separate source maps.

Index