🔷 TypeScript Q23 / 101

What is module augmentation?

AI-Powered Answer ✓ Answered

Module augmentation in TypeScript is a powerful feature that allows you to extend existing modules, adding new declarations (e.g., interfaces, types, functions, or properties) to them without modifying their original source code. This is particularly useful for extending third-party libraries or for cases where you need to add types for untyped JavaScript modules.

What is Module Augmentation?

Module augmentation allows you to 're-open' an existing module and add new types or extend existing ones. Instead of overwriting or creating a new definition, you're merging your new declarations with the original module's declarations. This capability is crucial for enhancing the type safety and developer experience when working with external libraries or frameworks that might lack complete type definitions or when you need to customize their types for your specific application.

It solves common problems such as adding custom properties to a third-party object (e.g., adding a user property to the Request object in Express.js), providing types for an untyped JavaScript library, or extending built-in global types like Array or Date with new methods or properties specific to your project.

How to Use Module Augmentation

Module augmentation is achieved using the declare module syntax. Depending on whether you're augmenting an external module (one imported via import 'module-name') or a global module/scope, the syntax differs slightly.

Augmenting External Modules

To augment an external module, you create a new declaration file (e.g., types/my-module.d.ts or simply a .ts file that contains import or export making it a module) and use declare module 'module-name' where 'module-name' is the exact string literal name of the module you want to augment. Inside this block, you can declare new interfaces, extend existing ones, or add new members.

typescript
// In custom.d.ts or a .ts file like src/types/express.d.ts
import { Request } from 'express';

declare module 'express' {
  interface Request {
    user?: { id: string; name: string; };
  }

  interface Response {
    customHeader(key: string, value: string): Response;
  }
}

After defining this, anywhere in your project where you use express, the Request object will now have an optional user property, and the Response object will have a customHeader method, providing type-safety for these additions.

Augmenting Global Scope (Global Augmentation)

To add declarations to the global scope (e.g., adding new properties to window or extending built-in types like Array or Date), you use declare global inside a module file. This block tells TypeScript that the declarations within it should be added to the global namespace. It's important that the file itself is a module (i.e., contains import or export statements, or is a .d.ts file alongside other modules, or uses export {} to explicitly make it a module).

typescript
// In global.d.ts or any module file
declare global {
  interface Date {
    toFormattedString(format: string): string;
  }

  interface Window {
    myGlobalFunction(): void;
  }
}

// If this file doesn't have imports/exports, add 'export {}' to make it a module
// export {};

Now, Date objects will have a toFormattedString method, and window will have myGlobalFunction, accessible and type-checked throughout your application.

Key Considerations

  • Location: Augmentations must be placed in a .d.ts file or a TypeScript file that is treated as a module (i.e., contains top-level import or export statements). If your file only contains augmentations and no other module syntax, add export {}; at the end to explicitly make it a module and enable global augmentation.
  • Merging Declarations: TypeScript merges all declarations from declare module blocks with the same module name, or declare global blocks, into a single definition.
  • Module Resolution: For external module augmentation, the declare module 'module-name' string must exactly match the string TypeScript uses to resolve that module.
  • No New Top-Level Exports: Module augmentation is for *extending* existing modules, not for defining new top-level exports for them. You cannot add new export const myNewVariable = ... directly inside an augmentation block.