In our previous post on TypeScript Mixins, we talked briefly about declaration merging in TypeScript. In these series of posts, we are going to go a little deeper starting with interfaces with interface declaration merging.
Declaration merging is when the TypeScript complier merges two or more types into one declaration provided they have the same name.
TypeScript allows merging between multiple types such as interface
with interface
, enum
with enum
, namespace
with namespace
, etc. One notable merge that isn’t permitted is class
with class
merging. If you want a functionality like that, checkout mixins.
Let’s get started with interface
with interface
merging by looking at an example:
interface Person {
name: string;
}
interface Person {
age: number;
}
interface Person {
height: number;
}
class Employee implements Person {
name = "Mensah"
age = 100;
height = 40
}
const employee = new Employee();
console.log(employee) // {name: "Mensah", age: 100, height: 40}
Since all the interfaces were declared with the same name, Person
, they are merged into one definition so the Employee
class contains the properties from all the interfaces.
If any of the interfaces to be merged contain the same property name and that property isn’t a function, then the type
of the properties must be the same or else the complier will throw an error.
interface Person {
name: string;
zipCode: string;
}
interface Person {
age: number;
zipCode: string; // acceptable
}
interface Person {
zipCode: number; // error
}
When the elements in the merged interfaces are functions and they have the same name, they are overloaded, that is, depending on the type
of argument passed, the appropriate function will be called.
interface Person {
speak(words: string);
}
interface Person {
speak(words: number);
}
const person: Person = {
speak: (wordsOrNum) => wordsOrNum
}
console.log(person.speak("Hi")) // speak(words: string) is used
console.log(person.speak(2)) // speak(words: number) is used
When interfaces containing same-signature functions are merged, the functions in the last declared interfaces appear at the top of the merged interface and the functions declared in the first interface appear beneath.
interface Person {
speak(words:string);
}
interface Person {
speak(words: any);
}
interface Person {
speak(words: number);
speak(words: boolean);
}
// This is how the final merged interface looks like
interface Person {
// functions in the last interface appear at the top
speak(words: number);
speak(words: boolean);
// function in the middle interface appears next
speak(words: any):number;
// function in the first interface appears last
speak(words: string):string;
}
The reason for this is, is that later interfaces declared have a higher precedence over the initial interfaces declared. So in our example above, speak(words: string)
will never be called because in the final merged Person
interface, speak(words: any):number
comes before speak(words: string):string
and since any
can stand for any type, it gets called even if a string
is passed as an argument
.
To prove this, when you hover over the per
variable in the code below, it will display const per: number
and not const per: string
even though we are passing in a string
argument.
const per = person.speak("bacon");
This is true for all interfaces expect when the same name functions parameters have a string literal as a type
. It follows the same order described above but functions with string literal types are given a higher precedence and therefore appear at the top.
It’s worth noting that, the string literals in later interfaces will appear before the initial interfaces like explained above.
interface Person {
speak(words: number);
speak(words: "World!"); // string literal type
}
interface Person {
speak(words: "Hello"); // string literal type
}
interface Person {
speak(words: string);
}
// merged interface output.
interface Person {
// string literals are given precedence
speak(words: "Hello");
speak(words: "World!");
// the rest of the functions are arranged using the same rules.
speak(words: string);
speak(words: number);
}
That’s it. Hope this was useful. 😊😎
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!