TypeScript Declaration Merging and Function Overloading: Interfaces, Namespaces & Overloads

1. What is declaration merging in TypeScript?

Declaration Merging: TypeScript's ability to combine multiple declarations of the same name into a single definition.

Can you give an example of declaration merging?


// Declaration merging example
interface User { name: string;
} // Merge additional properties into User
interface User { age: number;
} const user: User = { name: "kristal", age: 25
}; console.log(user); 

Output:


{ "name": "kristal", "age": 25 } 

2. What is interface merging in TypeScript?

Interface Merging: Combining multiple interface declarations with the same name into a single interface.

Can you give an example of interface merging?


// Interface merging example
interface Product { id: number; name: string;
} // Merge additional properties and methods
interface Product { price: number; getDetails(): string;
} class Item implements Product { id: number; name: string; price: number; constructor(id: number, name: string, price: number) { this.id = id; this.name = name; this.price = price; } getDetails(): string { return `${this.name} (ID: ${this.id}) costs $${this.price}`; }
} const item = new Item(1, "Laptop", 999.99);
console.log(item.getDetails()); 

Output:


Laptop (ID: 1) costs $999.99 

3. What is namespace merging in TypeScript?

Namespace Merging: Combining multiple namespace declarations with the same name into a single namespace.

Can you give an example of namespace merging?


// Namespace merging example
namespace Config { export const apiUrl = "https://api.example.com";
} namespace Config { export function getApiKey(): string { return "abc123"; }
} // Using merged namespace
console.log(`API URL: ${Config.apiUrl}`);
console.log(`API Key: ${Config.getApiKey()}`); 

Output:


API URL: https://api.example.com
API Key: abc123 

4. What is function overloading in TypeScript?

Function Overloading: Defining multiple function signatures for the same function name, allowing different parameter types or counts.

Can you give an example of function overloading?


// Function overloading example
function formatData(data: string): string;
function formatData(data: number): string;
function formatData(data: string | number): string { if (typeof data === "string") { return `String: ${data.toUpperCase()}`; } else { return `Number: ${data.toFixed(2)}`; }
} // Testing overloads
console.log(formatData("hello"));
console.log(formatData(42));
// console.log(formatData(true)); // Error: Argument of type 'boolean' is not assignable 

Output:


String: HELLO
Number: 42.00 

5. Can you provide a comprehensive example combining declaration merging, interface merging, namespace merging, and function overloading?

Project Structure:


ts-declaration-merging/
├── src/
│ └── main.ts
├── tsconfig.json
└── package.json 

main.ts:


// Comprehensive TypeScript Example
// Interface Merging
interface User { id: number; name: string;
} interface User { email: string; getProfile(): string;
} // Namespace Merging
namespace Settings { export const version = "1.0.0";
} namespace Settings { export interface Config { apiUrl: string; } export const config: Config = { apiUrl: "https://api.example.com" }; export function getVersionInfo(): string { return `Version: ${version}, API: ${config.apiUrl}`; }
} // Function Overloading
function processData(input: string): { type: "string"; value: string };
function processData(input: number): { type: "number"; value: number };
function processData(input: string | number): { type: string; value: string | number } { if (typeof input === "string") { return { type: "string", value: input.toUpperCase() }; } return { type: "number", value: input };
} // Class implementing merged interface
class UserProfile implements User { id: number; name: string; email: string; constructor(id: number, name: string, email: string) { this.id = id; this.name = name; this.email = email; } getProfile(): string { return `ID: ${this.id}, Name: ${this.name}, Email: ${this.email}`; }
} // Example usage
try { // Interface merging const user = new UserProfile(1, "kristal", "[email protected]"); console.log("User Profile:", user.getProfile()); // Namespace merging console.log("Settings:", Settings.getVersionInfo()); console.log("Config:", Settings.config); // Function overloading const stringResult = processData("hello"); const numberResult = processData(42); console.log("String Data:", stringResult); console.log("Number Data:", numberResult); // Error cases // const invalidUser: User = { id: 2, name: "Sashi" }; // Error: Property 'email' is missing // console.log(processData(true)); // Error: Argument of type 'boolean' is not assignable
} catch (e) { console.error(`Error: ${e.message}`);
} 

package.json:


{ "name": "ts-declaration-merging", "version": "1.0.0", "scripts": { "start": "tsc && node dist/main.js", "build": "tsc", "watch": "tsc --watch" }, "devDependencies": { "typescript": "^5.6.2" }
} 

tsconfig.json:


{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules"]
} 

Steps to Run:

  1. Create the project structure.
  2. Install dependencies: npm install.
  3. Compile: npm run build.
  4. Run: npm start.

Output:


User Profile: ID: 1, Name: kristal, Email: [email protected]
Settings: Version: 1.0.0, API: https://api.example.com
Config: { "apiUrl": "https://api.example.com" }
String Data: { "type": "string", "value": "HELLO" }
Number Data: { "type": "number", "value": 42 } 

Description:

6. What are common mistakes in these TypeScript features?

7. What are best practices for these TypeScript features?

  1. Declaration Merging:
    • Use for extending third-party libraries or global objects (e.g., Window).
    • Keep merged declarations in separate files for clarity.
  2. Interface Merging:
    • Ensure merged properties/methods have compatible types.
    • Use for modular type extensions in large projects.
  3. Namespace Merging:
    • Prefer ES modules over namespaces in modern TypeScript.
    • Use for global augmentation or legacy code support.
  4. Function Overloading:
    • Use overloads only when union types are insufficient.
    • Ensure implementation signature is broad enough to cover all overloads.
  5. General:
    • Follow TypeScript naming conventions (e.g., CamelCase for interfaces).
    • Use tsc with strict mode (strict: true in tsconfig.json).
    • Document complex types or overloads with JSDoc or comments for clarity.
    • Test with edge cases (e.g., invalid types, missing properties).
    • Use linters (e.g., eslint) with TypeScript plugins for consistency.