TypeScript with Node.js
Introduction
Node.js runs JavaScript, not TypeScript directly. For servers, CLIs, and scripts, you compile or transpile TypeScript to JavaScript, then run with node. This chapter covers a typical Node + TypeScript setup: tsconfig, @types/node, production builds with tsc, and developer workflows with tsx and file watching.
Prerequisites
- Installing TypeScript and Project Setup
- tsconfig Core Options
- Declaration Files and @types Packages
- JavaScript Node.js overview (helpful)
Project Skeleton
mkdir ts-node-api
cd ts-node-api
npm init -y
npm install --save-dev typescript @types/node tsx
npx tsc --inittsconfig.json for modern Node ESM:
package.json:
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node --enable-source-maps dist/index.js",
"dev": "tsx watch src/index.ts",
"typecheck": "tsc --noEmit"
}
}Code explanation:
"type": "module"enables native ESM in Nodebuildemitsdist/;startruns compiled outputdevusestsxfor fast reload during development
Hello Server in TypeScript
src/index.ts:
import http from "node:http";
const port = Number(process.env.PORT) || 3000;
const server = http.createServer((_req, res) => {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ ok: true, service: "ts-node-api" }));
});
server.listen(port, () => {
console.log(`Listening on http://localhost:${port}`);
});Run development:
npm run devProduction path:
npm run build
npm starttsc in CI
{
"scripts": {
"typecheck": "tsc --noEmit"
}
}Run npm run typecheck in CI even when you use tsx locally—catches type errors before deploy.
nodemon Alternative
npm install --save-dev nodemon{
"scripts": {
"dev": "nodemon --watch src --ext ts --exec \"tsx src/index.ts\""
}
}Safe JSON Parsing
Types do not validate runtime data—narrow unknown after JSON.parse.
ESM and __dirname
import { fileURLToPath } from "node:url";
import path from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);Use when resolving paths relative to the current module in ESM.
Common Pitfalls
| Issue | Fix |
|---|---|
require is not defined | Use import with "type": "module" |
Cannot find node:fs | Install @types/node; use NodeNext resolution |
| Import path errors | Use .js extension in imports for NodeNext emit |
| Types missing for package | Install @types/pkg or add local .d.ts |
FAQ
tsc vs tsx in production?
Run compiled dist/ with node. Use tsx for dev scripts.
Do Node APIs need a bundler?
Usually no for servers—tsc output is enough.
Vitest with TypeScript?
Install vitest and share tsconfig paths—see the JavaScript testing chapter.
Bun or Deno?
Same TypeScript ideas; runtime and config differ from this Node-focused lesson.