Declaration Merging is a unique mechanism in TypeScript that allows declarations with the same name to be combined into a single type/entity. This works for interfaces, namespaces, and functions.
interface User { name: string; } interface User { age: number; } const u: User = { name: 'Vasya', age: 42 }; // OK
function helper() {} namespace helper { export function extra() {} } helper.extra(); // OK
How does the merging of methods with the same name in different interface spaces occur, and what happens if their signatures differ?
Answer:
If you declare methods with the same name in different merge interfaces, TypeScript will attempt to "overload" these methods in the resulting interface. However, if the signatures are incompatible (cannot be overloaded), this will result in a compilation error.
Example:
interface Foo { bar(a: number): void } interface Foo { bar(a: string): void } // OK: overloads interface Foo { bar(a: number[]): void } // Error: incompatible signatures
Story
In one project, the third-party Window interface was extended through declaration merging for different features: one team added window.myFeature: boolean, another added window.myFeature: number. The merge caused a type error, and the compiler only caught the conflict during the overall build — someone had to quickly do a rename.
Story
By mistake, two ArrayHelper interfaces with different method signatures were declared, expecting that they would combine into "both options available". In reality, the first signature overshadowed the second, resulting in incorrect autocompletion in the IDE and a bug during integration with a new module.
Story
Using a namespace to "extend" a function through declaration merging, a developer incorrectly declared export inside the namespace, and the function became unavailable. Only after the review was it discovered that without the correct export, the merge does not work and the properties do not appear on the function.