Declaration merging in TypeScript allows you to combine multiple declarations with the same name into one. This can be applied to interfaces, namespaces, and enums. When you have multiple declarations of the same name, TypeScript will merge them together, combining their members.
// Interface declaration merging
interface User {
name: string;
}
interface User {
age: number;
}
const user: User = {
name: 'John',
age: 30
};
In this example, the two User
interfaces are merged into one, and the user
object must have both the name
and age
properties.
Interface extension in TypeScript allows you to create a new interface that inherits properties and methods from an existing interface. You can use the extends
keyword to extend an interface.
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
const square: Square = {
color: 'blue',
sideLength: 10
};
Here, the Square
interface extends the Shape
interface, so the square
object must have both the color
property from the Shape
interface and the sideLength
property from the Square
interface.
Declaration merging can be used in various scenarios, such as extending built - in types or adding new functionality to existing modules.
// Extending built - in Array type
interface Array<T> {
first(): T | undefined;
}
Array.prototype.first = function() {
return this.length > 0? this[0] : undefined;
};
const numbers = [1, 2, 3];
const firstNumber = numbers.first();
console.log(firstNumber); // Output: 1
In this example, we use declaration merging to add a new method first
to the built - in Array
type.
Interface extension is commonly used when you want to create a hierarchy of related types. For example, in a game development scenario, you might have different types of characters.
interface Character {
name: string;
health: number;
}
interface Warrior extends Character {
weapon: string;
attack(): void;
}
const warrior: Warrior = {
name: 'Warrior Bob',
health: 100,
weapon: 'Sword',
attack() {
console.log('Attacking with the sword!');
}
};
Here, the Warrior
interface extends the Character
interface, creating a more specialized type.
Module augmentation is a powerful use case for declaration merging. You can use it to add new properties or methods to existing modules.
// Suppose we have a module named 'myModule'
declare module 'myModule' {
export interface MyConfig {
apiKey: string;
}
}
// Later in our code
import { MyConfig } from 'myModule';
const config: MyConfig = {
apiKey: '123456'
};
This way, we can extend the types defined in an external module without modifying its source code.
Interface extension promotes code reusability by allowing you to create a base interface and then extend it to create more specific interfaces.
interface Animal {
name: string;
eat(): void;
}
interface Bird extends Animal {
fly(): void;
}
interface Fish extends Animal {
swim(): void;
}
const bird: Bird = {
name: 'Eagle',
eat() {
console.log('Eating fish');
},
fly() {
console.log('Flying high');
}
};
const fish: Fish = {
name: 'Salmon',
eat() {
console.log('Eating plankton');
},
swim() {
console.log('Swimming fast');
}
};
Here, the Animal
interface provides a common set of properties and methods, and the Bird
and Fish
interfaces extend it to add their own unique behavior.
When using declaration merging, make sure that the merged declarations are consistent. For example, if you are merging functions, the function signatures should be compatible.
// Incorrect merging of functions
interface MyFunctions {
add(a: number, b: number): number;
}
interface MyFunctions {
add(a: string, b: string): string; // This can lead to confusion
}
In this case, the two add
function declarations have different parameter types, which can make the code hard to understand and maintain.
When using interface extension, avoid creating overly deep hierarchies. Deep hierarchies can make the code complex and hard to follow. Try to keep the inheritance tree shallow and focused.
// Shallow inheritance example
interface Vehicle {
move(): void;
}
interface Car extends Vehicle {
drive(): void;
}
// Instead of creating a very deep hierarchy
TypeScript’s Declaration Merging and Interface Extension are powerful features that enhance the flexibility and reusability of your code. Declaration merging allows you to combine multiple declarations with the same name, while interface extension enables you to create a hierarchy of related types. By understanding the fundamental concepts, usage methods, common practices, and best practices, you can use these features effectively in your TypeScript projects to write cleaner, more maintainable code.