Skip to main content
Practice Problems

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:

typescript
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 properties

type Cannot Be Merged

typescript
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:

typescript
// 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

typescript
// 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

typescript
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

typescript
enum Color { Red = 0, Green = 1, } enum Color { Blue = 2, Yellow = 3, } // Merged: Color.Red, Color.Green, Color.Blue, Color.Yellow

Practical Use Cases

Adding Types to Libraries Without Types

typescript
// custom.d.ts declare module "untyped-library" { export function doSomething(input: string): number; export const VERSION: string; }

Environment Variables

typescript
// 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 string

Merging Rules

DeclarationCan merge with
InterfaceInterface βœ…
NamespaceNamespace βœ…, Class βœ…, Function βœ…, Enum βœ…
ClassNamespace βœ…
FunctionNamespace βœ…
EnumEnum βœ…, Namespace βœ…
Type aliasNothing ❌

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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems