Skip to content

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:

  • fs
  • path
  • http
  • crypto
  • os
  • events
  • stream
  • 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 PathLike type

Example: Typing path.join

import path from "path"

const fullPath: string = path.join(__dirname, "assets", "logo.png")

TypeScript knows:

  • path.join always returns a string
  • __dirname is 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:

  • readFile returns Promise<string>
  • JSON.parse returns any (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:

  • err is NodeJS.ErrnoException | null
  • data is string

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:

  • readFileSync returns a string
  • path.join returns a string
  • Express Request and Response types are correct
  • config.version exists

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.