Day 52: Namespace Augmentation
Tags: javascript, typescript, 100daysofcode
Published: 2023-09-20 13:30:00


## What is Namespace Augmentation?

In TypeScript, a namespace is a way to organize code into logical containers. They are often used to group related functions, classes, and interfaces together. Namespace augmentation allows you to extend these namespaces after they have been declared, even if they are in separate files. This is extremely useful when you want to add functionality to third-party libraries or when working on large codebases where modifying the original declaration might not be feasible.

## Syntax

To augment a namespace in TypeScript, you use the `declare` keyword followed by the original namespace name. Then, you can add new members or modify existing ones.

```typescript
// Original namespace declaration
namespace OriginalNamespace {
  export interface MyInterface {
    name: string;
    age: number;
  }
}

// Namespace augmentation
declare namespace OriginalNamespace {
  export interface MyInterface {
    email: string;
  }

  export function sayHello(): void;
}
```

In the above code, we've added an `email` property and a `sayHello` function to the `MyInterface` and `OriginalNamespace`, respectively.

## Use Cases for Namespace Augmentation

### 1. Extending Third-Party Libraries

One of the most common use cases for namespace augmentation is extending third-party libraries. Imagine you're using a popular library like `axios` for making HTTP requests, and you want to add some custom functionality or interceptors. You can do this easily with namespace augmentation without modifying the library's source code.

```typescript
// Import axios
import axios, { AxiosRequestConfig } from 'axios';

// Augment axios
declare namespace Axios {
  interface MyInterceptor {
    onRequest(config: AxiosRequestConfig): AxiosRequestConfig;
  }
}

// Use the augmented axios
const myInterceptor: Axios.MyInterceptor = {
  onRequest(config) {
    // Add custom logic here
    return config;
  }
};

axios.interceptors.request.use(myInterceptor.onRequest);
```

### 2. Enhancing Built-in Objects

You can also use namespace augmentation to enhance built-in objects like `Array` or `String`. Let's say you want to add a utility function to check if an array contains a specific element.

```typescript
// Augmenting the Array prototype
declare global {
  interface Array<T> {
    contains(item: T): boolean;
  }
}

// Implementation
Array.prototype.contains = function<T>(item: T): boolean {
  return this.indexOf(item) !== -1;
};

// Usage
const myArray = [1, 2, 3, 4, 5];
const containsThree = myArray.contains(3); // true
const containsTen = myArray.contains(10);   // false
```

### 3. Modifying Third-Party Type Definitions

If you're using TypeScript with DefinitelyTyped type definitions for external libraries, you can use namespace augmentation to fix or extend these definitions without waiting for updates from the community.

```typescript
// Augmenting the type definition for an external library
declare module 'external-library' {
  interface SomeType {
    newProperty: string;
  }
}

// Usage
import { SomeType } from 'external-library';
const myObject: SomeType = { newProperty: 'Hello' };
```

## Advanced Namespace Augmentation

Namespace augmentation becomes even more powerful when you combine it with modules. By exporting and importing augmented namespaces, you can create modular and maintainable code. 

```typescript
// File: math.ts
namespace MathUtil {
  export function add(a: number, b: number): number {
    return a + b;
  }
}

export default MathUtil;

// File: math-augmentation.ts
import MathUtil from './math';

declare module './math' {
  interface MathUtil {
    subtract(a: number, b: number): number;
  }
}

MathUtil.subtract = (a, b) => a - b;

// File: main.ts
import MathUtil from './math';
import './math-augmentation';

const sum = MathUtil.add(5, 3);        // 8
const difference = MathUtil.subtract(8, 3); // 5
```