TypeScript on Node.js

Run TypeScript on the server with confidence. You'll set up a Node-friendly tsconfig.json , add @types/node , understand ESM vs CommonJS , use tsx / ts-node in development, compile with tsc for production, and work with typed process and fs .

Learn TypeScript on Node.js in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

TypeScript on Node is like writing in a language Node doesn't speak and using a translator . tsc is the careful translator that produces a polished JavaScript document ahead of time; tsx is the live interpreter that translates as you go. Either way, Node only ever hears JavaScript — the TypeScript is for you and the compiler.

1. A Node-Ready tsconfig.json

The config tells tsc what to emit and how to resolve imports. For modern Node, set "module" and "moduleResolution" to "NodeNext" , choose an outDir , and turn on strict . Read the real file here:

2. The Build-and-Run Cycle

Node runs JavaScript , so your TypeScript has to become JS first. Two modes cover everything: compile with tsc then run the output (production), or run the .ts directly with tsx / ts-node (development). The box below prints both flows.

3. The Typed process Global

Once @types/node is installed, Node's globals are fully typed. process.argv is a string[] of command-line arguments, and process.env holds environment variables (always string | undefined ). The box simulates process so it runs here:

4. ESM vs CommonJS

CommonJS uses require() and module.exports ; ES modules use import and export , enabled by "type": "module" . A classic gotcha: __dirname exists in CommonJS but not in ESM. The read-only block above shows the real syntax; the box below models both systems:

Your turn. Fill in the two blanks marked ___ to read a --port argument, then run it.

No blanks this time — build loadConfig() yourself (env, coerced port, boolean debug), run it, and match your output to the example.

Practice quiz

Why install @types/node in a Node TypeScript project?

  • It provides type definitions for Node's built-in modules and globals
  • It compiles your code
  • It is the Node runtime
  • It speeds up Node

Answer: It provides type definitions for Node's built-in modules and globals. @types/node ships the type declarations for process, fs, path, Buffer, and other Node built-ins so the compiler knows them.

What does the tsc command do?

  • Installs Node types
  • Starts a dev server
  • Compiles TypeScript to JavaScript that Node can run
  • Runs the TypeScript file directly

Answer: Compiles TypeScript to JavaScript that Node can run. tsc (the TypeScript compiler) emits plain JavaScript; Node then runs that compiled .js output.

Which tool lets you run a .ts file directly during development without a separate build step?

  • npm
  • tsx (or ts-node)
  • eslint
  • prettier

Answer: tsx (or ts-node). tsx and ts-node transpile and execute TypeScript on the fly, which is convenient for development.

In tsconfig.json, what does the "module" option control?

  • The lint rules
  • The output folder
  • The Node version
  • The module system of the emitted JavaScript (e.g. CommonJS or ESM)

Answer: The module system of the emitted JavaScript (e.g. CommonJS or ESM). "module" sets the module format tsc emits, such as "CommonJS" or "NodeNext"/"ESNext" for ES modules.

How does Node decide a project uses ES modules rather than CommonJS?

  • "type": "module" in package.json (or a .mjs extension)
  • It always uses ESM
  • The presence of @types/node
  • The file is large

Answer: "type": "module" in package.json (or a .mjs extension). Setting "type": "module" in package.json (or using .mjs) tells Node to treat .js files as ES modules; otherwise they are CommonJS.

Which syntax loads a module in CommonJS?

  • include "x"
  • require("x")
  • import x from "x"
  • load("x")

Answer: require("x"). CommonJS uses require() and module.exports; ES modules use import/export.

What is a clean way to get the current directory in an ES module, where __dirname is not defined?

  • It is impossible in ESM
  • Just use __dirname, it always works
  • Use process.argv
  • Derive it from import.meta.url with fileURLToPath

Answer: Derive it from import.meta.url with fileURLToPath. In ESM, __dirname and __filename are not defined; you reconstruct them from import.meta.url using fileURLToPath and path.dirname.

For a typical Node project, what moduleResolution setting matches Node's behaviour with ESM?

  • "none"
  • "classic"
  • "NodeNext" (or "Node16")
  • "browser"

Answer: "NodeNext" (or "Node16"). "NodeNext"/"Node16" tells tsc to resolve modules the way modern Node does, including ESM rules and file extensions.

After running tsc, what do you actually execute with Node?

  • The node_modules folder
  • The compiled .js output
  • The tsconfig.json
  • The .ts source files

Answer: The compiled .js output. tsc emits JavaScript (often into a dist/ folder); you then run that compiled .js, for example node dist/index.js.

Where does Node read command-line arguments and environment variables from?

  • The process global (process.argv and process.env)
  • The window object
  • The fs module only
  • import.meta

Answer: The process global (process.argv and process.env). Node's process global exposes process.argv for arguments and process.env for environment variables; @types/node types them.