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

  1. Use declaration merging - For extending types
  2. Create .d.ts files - For type-only augmentations
  3. Document augmentations - Explain what you're adding
  4. Avoid breaking changes - Don't remove existing properties
  5. Use namespaces - For organizing augmentations

Common Mistakes

  1. Augmenting non-existent modules
// ❌ Module doesn't exist
declare module "non-existent-module" {
  // ...
}

// ✅ Augment existing module
declare module "existing-module" {
  // ...
}
  1. 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!
}
  1. 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.