Difference between extending and intersecting interfaces in TypeScript?
Difference between extending and intersecting interfaces in TypeScript?
Ask Question Asked 3 years, 7 months ago Modified 10 months ago Viewed 18k timesLet's say the following type is defined:
interface Shape {
color: string;
}
Now, consider the following ways to add additional properties to this type:
Extension
interface Square extends Shape {
sideLength: number;
}
Intersection
type Square = Shape & {
sideLength: number;
}
What is the difference between both approaches?
And, for sake of completeness and out of curiosity, are there other ways to yield comparable results?
typescripttypesintersectionextends se-share-sheet#willShow s-popover:shown->se-share-sheet#didShow">Share Improve this question edited Oct 6, 2018 at 17:01 asked Oct 6, 2018 at 16:56- 2 See Also: Interfaces vs. Intersections – KyleMit? Dec 20, 2021 at 19:39
This leads to another interesting difference, interface
declarations are open ended. New members can be added anywhere because multiple interface
declarations with same name in the same declaration space are merged.
Here is a common use for merging behavior
lib.d.ts
interface Array {
// map, filter, etc.
}
array-flat-map-polyfill.ts
interface Array {
flatMap(f: (x: T) => R[]): R[];
}
if (typeof Array.prototype.flatMap !== 'function') {
Array.prototype.flatMap = function (f) {
// Implementation simplified for exposition.
return this.map(f).reduce((xs, ys) => [...xs, ...ys], []);
}
}
Notice how no extends
clause is present, although specified in separate files the interfaces are both in the global scope and are merged by name into a single logical interface declaration that has both sets of members. (the same can be done for module scoped declarations with slightly different syntax)
By contrast, intersection types, as stored in a type
declaration, are closed, not subject to merging.
There are many, many differences. You can read more about both constructs in the TypeScript Handbook. The Interfaces and Advanced Types section are particularly relevant.
se-share-sheet#willShow s-popover:shown->se-share-sheet#didShow">Share Improve this answer edited Jul 18, 2021 at 19:26
- 2 Great answer. Thanks for pointing out the difference in behaviour when 'overriding' properties, didn't know about that. That alone is a good reason to use types in certain use cases. Can you point out situations where interface merging is useful? Are there valid use cases when building applications (in other words: not libraries)? – Willem-Aart Oct 6, 2018 at 18:42
-
Willem Aart as you suggest, it is most useful for writing libraries, but what is an application if not a collection of libraries (including your own app). It can be extremely useful for applications as well. Ex:
interface Object {hasOwnProperty
which turns(this: T, key: K): this is {[P in K]?}} Object.prototype.hasOwnProperty
into a type guard by introducing an additional, more specific signature for it. . – Aluan Haddad Oct 6, 2018 at 19:12 -
1
@AluanHaddad the
StringToNumberConverter
type should be instead namedBidirectionalStringNumberConverter
, correct? It seems like the other instances were possibly renamed... – Karl Horky Jul 4, 2019 at 19:56 - 1 @NathanChappell thank you for catching that. I don't know when that broke. I've updated the example to make it compile, but it now requires a type assertion. I will look into this more. – Aluan Haddad Sep 22, 2020 at 8:37
- 1 @AluanHaddad thanks. TS seems to be changing quite fast, so it's probably impossible to keep up with it (especially since they seem to have abandoned maintaining a specification...) – Nathan Chappell Sep 22, 2020 at 8:47