Module and Global Augmentation

Adding Fields to a Library Type

Module augmentation adds to the types of an existing module by re-opening its interface with declare module "name". It relies on interface declaration merging. The classic example is adding user to Express requests after an auth middleware runs.

// src/types/express.d.ts
import "express";

declare module "express-serve-static-core" {
  interface Request {
    user?: { id: string; role: "admin" | "customer" };
    requestId: string;
  }
}

// anywhere in the app
app.get("/me", (req, res) => {
  res.json({ id: req.user?.id, requestId: req.requestId }); // typed
});

Other Common Augmentations

// NextAuth / Auth.js: add the user id and role to the session
declare module "next-auth" {
  interface Session {
    user: { id: string; role: "admin" | "customer"; name?: string | null; email?: string | null };
  }
}

// Typed environment variables for process.env
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      DATABASE_URL: string;
      NODE_ENV: "development" | "production" | "test";
    }
  }
}

// Importing assets in a bundler project
declare module "*.svg" {
  const src: string;
  export default src;
}
export {};

Note: A file with declare module "x" must be a module itself (have an import or export) to augment x. Without it, the declaration replaces the module types entirely.