tezvyn:

Module Augmentation: Adding Types to External Libraries

AI-drafted, machine-checkedSource: typescriptlang.orgintermediate

Module augmentation is like monkey-patching for types, letting you add definitions to external modules. Use it to add a `user` property to Express's `Request` object.

WHY IT EXISTS: JavaScript is dynamic, and developers often add properties to objects from third-party libraries at runtime—for example, middleware adding a user object to a request. TypeScript needs a formal, type-safe way to understand these additions at compile time to provide accurate checking and autocompletion. Module augmentation is that mechanism.

THE MENTAL MODEL: Think of module augmentation as creating a type-safe "patch" for an external library. You aren't changing the library's code, but you are telling the TypeScript compiler, "In my project, this object from this library will also have these extra properties." It's like adding an official appendix to a book you didn't write, making your additions feel like part of the original.

HOW IT WORKS: Module augmentation relies on a core TypeScript feature called "declaration merging." When TypeScript sees multiple declarations for the same interface, it merges them into a single definition. To augment a module, you create a file (e.g., my-types.d.ts) and use the declare module 'module-name' { ... } syntax. Inside this block, you can extend the module's original interfaces. For example, export interface Request { user?: User } inside declare module 'express' will merge your user property into the original Request interface from the Express library.

WHEN TO USE IT: Use it whenever you add properties to an object from an external library and want TypeScript to know about them. Common cases include: adding properties to a web framework's request object (like Express or Fastify), extending a UI library's theme object with custom brand colors, or patching a library that has incorrect or incomplete type definitions.

WHEN NOT TO USE IT: Don't use it to change the type of an existing property; this often indicates a design flaw. Augmentation is for adding, not modifying. If you find yourself augmenting a library extensively, consider if a wrapper class or a different library would be a cleaner solution. It’s a tool for targeted extensions, not for rewriting a library's entire type contract from the outside.

ONE CANONICAL EXAMPLE: A classic use case is adding a user object to the Express Request object after an authentication middleware runs. In a global declaration file (like types.d.ts), you would write:

declare module 'express-serve-static-core' { interface Request { user?: { id: string; email: string; }; } }

Now, anywhere in your project, TypeScript will recognize that req.user is a valid property on an Express Request object, even though the original library type doesn't define it.

Read the original → typescriptlang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.