Working with Node.js¶
Node.js is a JavaScript runtime with its own APIs—file system access, path manipulation, networking, streams, buffers, and more. TypeScript provides excellent support for Node, but there are a few concepts you must understand to use it effectively.
Typing Node Core Modules (fs, path, etc.)¶
Node’s built‑in modules come with first‑class TypeScript types. You don’t need to install anything extra for:
fspathhttpcryptooseventsstream- and many more
Example: Typing fs.readFileSync¶
import { readFileSync } from "fs"
const content: string = readFileSync("data.txt", "utf8")
Hovering over readFileSync shows:
readFileSync(path: PathLike, options?: { encoding?: null; flag?: string; } | null): Buffer
readFileSync(path: PathLike, options: { encoding: BufferEncoding; flag?: string; } | BufferEncoding): string
TypeScript understands:
- overloads
- return types based on parameters
- Node’s
PathLiketype
Example: Typing path.join¶
import path from "path"
const fullPath: string = path.join(__dirname, "assets", "logo.png")
TypeScript knows:
path.joinalways returns a string__dirnameis a string- arguments must be strings
Example: Typing fs.promises¶
import { promises as fs } from "fs"
async function loadConfig() {
const raw = await fs.readFile("config.json", "utf8")
return JSON.parse(raw)
}
TypeScript infers:
readFilereturnsPromise<string>JSON.parsereturnsany(you’ll fix this later with generics or Zod)
Typing Common Node Patterns¶
Node code often uses patterns like:
- callbacks
- streams
- event emitters
- buffers
TypeScript supports all of these.
Callback example¶
import fs from "fs"
fs.readFile("data.txt", "utf8", (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data)
})
TypeScript infers:
errisNodeJS.ErrnoException | nulldataisstring
EventEmitter example¶
import { EventEmitter } from "events"
const emitter = new EventEmitter()
emitter.on("message", (text: string) => {
console.log(text)
})
emitter.emit("message", "Hello!") // ✔ OK
emitter.emit("message", 123) // ❌ Error
TypeScript enforces event payload types.
Using DefinitelyTyped (@types/...)¶
Not all npm packages ship with TypeScript types.
When a package doesn’t include its own .d.ts files, you install types from DefinitelyTyped, a massive community‑maintained repository.
How to check if a package has built‑in types¶
Look for "types" or "typings" in package.json.
Example (Express):
{
"name": "express",
"types": "index.d.ts"
}
If it’s missing, you install types separately.
Installing type definitions¶
Example: Express¶
npm install express
npm install --save-dev @types/express
Example: Lodash¶
npm install lodash
npm install --save-dev @types/lodash
Example: UUID¶
npm install uuid
npm install --save-dev @types/uuid
How TypeScript uses these types¶
Once installed, TypeScript automatically picks them up—no configuration needed.
Typing Node + Third‑Party Code Together¶
Here’s a realistic example combining Node core modules and DefinitelyTyped types:
import fs from "fs"
import path from "path"
import express, { Request, Response } from "express"
const app = express()
app.get("/config", (req: Request, res: Response) => {
const filePath = path.join(__dirname, "config.json")
const raw = fs.readFileSync(filePath, "utf8")
const config = JSON.parse(raw) as { version: string }
res.json(config)
})
TypeScript ensures:
readFileSyncreturns a stringpath.joinreturns a string- Express
RequestandResponsetypes are correct config.versionexists
This is the kind of safety you want in real Node applications.
Summary¶
In this lesson, you learned how TypeScript integrates with Node.js:
1. Typing Node core modules¶
fs,path,http,crypto, etc.- Strong built‑in types
- Overloads and return type inference
2. Typing common Node patterns¶
- callbacks
- event emitters
- streams
- buffers
3. Using DefinitelyTyped (@types/...)¶
- Install types for packages that don’t include their own
- TypeScript picks them up automatically
- Essential for Express, Lodash, UUID, and many others
This knowledge prepares you for building fully typed Node.js applications with confidence.