Declaration Merging in TypeScript Interfaces
We have two interfaces with the same name, Logger
:
interface Logger { log(message: string, level: number): void;}interface Logger { log(message: string): void;}
In JavaScript, this would cause an error because you cannot redeclare a variable in the same scope.
However
Transcript
00:00 Okay, so what you'll notice if you look at this quite closely is that we have two interfaces with duplicate names We've got interface logger and interface logger You would expect that these kind of behave as if they were JavaScript variables where if we have two loggers with the same name And these would error here, right?
00:16 You would get a cannot redeclare block scoped variable logger and this would fail at runtime to I'm pretty sure although I've never tried And you'd think that's what happens here. But no when you have declaration Merging here. This is what this is called You have two different declarations with the same name of interfaces, then they merge their declarations
00:37 so if I were to instant for instance add it like a string on this one and B string on this one then currently my logger Requires me to pass a and requires me to pass B. Look at that So these declarations are getting merged Whereas if I delete the one I don't want and this is the one I don't want because it's got the excess
00:57 Parameter there. Let's pull that off Now I'm only required to pass B and my error down the bottom goes away So my logger dot log now only requires a message string instead of needing message string level number So you might be looking at that and thinking or that looks pretty awful
01:15 Why does TypeScript let you even do interface declaration merging? Why is it allowed you to have two with the same name? Well, if you use type then you don't have this problem Look at this if we hover over this and it says duplicate identifier logger, it's gonna warn you
01:33 It doesn't merge them. It just gives you an error and says duplicate identifier, but interface it doesn't Now this will only happen when they're in the same scope when they're in the same module scope So if I declare an interface and a different file, which is in a module scope, then it's not going to interfere with that one
01:49 I don't need to worry about this cross project unless I'm doing that deliberately, which we'll get to later so it's only when they're in the same scope really declared in the same file, but this is Annoying enough that you might want to use types ahead of interfaces most of the time Especially if you're in long files, for instance So yeah
02:09 Like the reason this exists is because you need some mechanism of doing this which we will get to later in this course Where we talk about kind of like global appending to interfaces like window for instance So we have for instance window is a global interface and I can if I want to actually where is it interface? Yeah, there's the interface for window
02:28 This has a bunch of stuff on it like documents and events external blah blah blah blah And if I want to globally add things to the window, I need a way of doing that But it can be pretty annoying as an application dev when you have just these two interfaces Knocking about and they accidentally have the same name and you get some unexpected behavior
02:47 So this is kind of why I tend to recommend type as kind of like a default way of declaring object types but then of course you also need to think about intersections versus Interface extends and their interface wins. So there's no clear winner still between type versus interface But this behavior is something that should give you pause about using interface by default