Extend existing modules without modifying their source code.
Augmenting Modules
// original-module.ts
export interface User {
name: string;
age: number;
}
// augmentation.ts
declare module "./original-module" {
interface User {
email: string; // Added property
}
}
// usage.ts
import { User } from "./original-module";
const user: User = {
name: "Alice",
age: 25,
email: "alice@example.com" // Now available
};
Augmenting Node Modules
// types/express.d.ts
import { Request } from "express";
declare module "express-serve-static-core" {
interface Request {
user?: {
id: string;
name: string;
};
}
}
// usage.ts
import express from "express";
const app = express();
app.use((req, res, next) => {
req.user = { id: "1", name: "Alice" }; // TypeScript knows about user
next();
});
Global Augmentation
// global.d.ts
declare global {
interface Window {
myCustomProperty: string;
}
namespace NodeJS {
interface ProcessEnv {
CUSTOM_VAR: string;
}
}
}
// usage.ts
window.myCustomProperty = "value"; // TypeScript knows about it
const env = process.env.CUSTOM_VAR; // TypeScript knows about it
Augmenting Classes
// original.ts
export class Calculator {
add(a: number, b: number): number {
return a + b;
}
}
// augmentation.ts
import { Calculator } from "./original";
declare module "./original" {
interface Calculator {
multiply(a: number, b: number): number;
}
}
Calculator.prototype.multiply = function(a: number, b: number): number {
return a * b;
};
// usage.ts
import { Calculator } from "./original";
const calc = new Calculator();
calc.add(1, 2); // Original method
calc.multiply(2, 3); // Augmented method
Augmenting Enums
// original.ts
export enum Status {
Pending,
Approved
}
// augmentation.ts
declare module "./original" {
enum Status {
Rejected = 2
}
}
// Note: Enum augmentation is limited and not recommended
// Better to use const objects or union types
Exercises
Exercise 1: Module Augmentation
// Augment a module to add:
// - A new interface property
// - A new function export
// - Use them in your code
Exercise 2: Global Augmentation
// Augment the global scope to add:
// - A custom property to Window
// - A custom type to global namespace
// - Use them in your code
Exercise 3: Third-Party Library
// Augment a third-party library type:
// - Add properties to an interface
// - Extend a class with new methods
// - Use the augmented types
Exercise 4: Process Env
// Augment process.env to include:
// - Custom environment variables
// - Type-safe access to them
Exercise 5: Express Request
// Augment Express Request to include:
// - User property
// - Custom headers
// - Use them in middleware
Best Practices
- Use declaration merging - For extending types
- Create .d.ts files - For type-only augmentations
- Document augmentations - Explain what you're adding
- Avoid breaking changes - Don't remove existing properties
- Use namespaces - For organizing augmentations
Common Mistakes
- Augmenting non-existent modules
// ❌ Module doesn't exist
declare module "non-existent-module" {
// ...
}
// ✅ Augment existing module
declare module "existing-module" {
// ...
}
- Conflicting augmentations
// ❌ Two files augmenting differently
// file1.d.ts
declare module "module" {
interface A { x: string; }
}
// file2.d.ts
declare module "module" {
interface A { y: number; } // Conflict!
}
- Not exporting augmented types
// ❌ Augmentation not visible
declare module "./module" {
interface Internal { } // Not exported
}
// ✅ Export if needed
declare module "./module" {
export interface Public { } // Exported
}
Next Steps
Learn about Declaration Merging.