Declaration merging in TypeScript
What is Declaration Merging?
Declaration merging is TypeScript's ability to combine multiple declarations with the same name into a single definition. This is a unique feature that doesn't exist in most other languages.
Interface Merging
The most common form β multiple interface declarations with the same name are merged:
interface User {
name: string;
}
interface User {
age: number;
}
interface User {
email: string;
}
// All three are merged into:
// interface User {
// name: string;
// age: number;
// email: string;
// }
const user: User = {
name: "Alice",
age: 25,
email: "alice@example.com"
}; // Must have ALL propertiestype Cannot Be Merged
type User = { name: string };
type User = { age: number }; // β Error: Duplicate identifier 'User'This is a key difference between interface and type.
Module Augmentation
Extend existing modules by adding declarations:
// Extend Express Request
declare module "express" {
interface Request {
user?: {
id: string;
role: string;
};
}
}
// Now req.user is available in all handlers
app.get("/profile", (req, res) => {
console.log(req.user?.id); // β
TypeScript recognizes this
});Extending Third-party Libraries
// Add custom matchers to Jest
declare module "@jest/expect" {
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
}
}
// Extend Window
declare global {
interface Window {
analytics: {
track(event: string, data?: object): void;
};
}
}
window.analytics.track("page_view"); // β
Namespace Merging
namespace Validation {
export function isEmail(value: string): boolean {
return /^[^@]+@[^@]+$/.test(value);
}
}
namespace Validation {
export function isPhone(value: string): boolean {
return /^\+?\d{10,}$/.test(value);
}
}
// Both functions available:
Validation.isEmail("test@test.com"); // β
Validation.isPhone("+380123456789"); // β
Enum Merging
enum Color {
Red = 0,
Green = 1,
}
enum Color {
Blue = 2,
Yellow = 3,
}
// Merged: Color.Red, Color.Green, Color.Blue, Color.YellowPractical Use Cases
Adding Types to Libraries Without Types
// custom.d.ts
declare module "untyped-library" {
export function doSomething(input: string): number;
export const VERSION: string;
}Environment Variables
// env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: "development" | "production" | "test";
DATABASE_URL: string;
JWT_SECRET: string;
PORT?: string;
}
}
process.env.DATABASE_URL; // β
Typed as stringMerging Rules
| Declaration | Can merge with |
|---|---|
| Interface | Interface β |
| Namespace | Namespace β , Class β , Function β , Enum β |
| Class | Namespace β |
| Function | Namespace β |
| Enum | Enum β , Namespace β |
| Type alias | Nothing β |
Important:
Declaration merging is most useful for module augmentation β extending third-party library types. Interface merging is also why interface is preferred over type for public APIs β consumers can extend them. Use declare module for library augmentation and declare global for global scope extensions.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.