Use Declaration Merging to Add Functionality to the Global Window
Let's start by examining the
When hovering over
window.makeGreetingSolution, we can see information about its type:
// hovering over windowvar window: Window & typeof globalThis
Using VS Code's Go to definition feature, we get taken over to
lib.dom.d.ts and ends u
0:01 The solution here is we know we're going to need a declare global first of all. The Window interface here, or the thing that is representing this Window, we can hover over it, and if we do a go to definition, then we're going to see that we go into lib.dom.d.ts, and we're inside declare var.
0:24 d.ts files automatically, whatever they do, they automatically put all their stuff inside the global ambient context. That means that if we add in a random file into here, let's say we go example.d.ts, then if we add an interface Cool or something which has a wow 1 on it, then if we go to where we were, then we can actually just access that.
0:48 We can say const wow is...God, already can't remember what I had here. Wow is Cool, and this is going to error, because it doesn't match up to the Cool interface property. Wow is missing, wow 1. I should really name my stuff more sensible things. As you can see, this interface, .d.ts files automatically put their stuff in the global scope. Let me delete that one so it doesn't us any issues.
1:13 Then inside here, this Window, if we have a look at it, we've got this Window. You can see if we do a go to definition, we have two bits on it. We have an interface and we also have a declare var. The interface is what we're interested in, because there's a property of interface, this is a huge, huge piece of stuff here, and this is inside our lib.dom.d.ts. It means that it's available globally.
1:41 Our Window then...I'm sorry. I think I lost it for a second. Window is a huge, huge file. What we can do is we need to add a property to this. If I were to just modify this file, which I can do, I can just go back to here and I can say I just want to add makeGreetingSolution onto it, which is function that returns a string.
2:08 Now, if I remove this, you can see that it actually works, because I've added this thing onto there. Except this isn't really an option, because this is just a local file on my machine. It's not going to be committed to your project. This isn't code inside your project. You really should not be editing this file, because it'll mess everything up. Let's just ignore the fact that I did that.
2:29 We need to find a way to modify this interface without actually touching the file. The way to do that is inside declare global. In fact, let me show you one thing first.
2:39 We have this interface, let's say interface -- I'm going to choose a proper name for it -- interface User, but we have an id string. Let's say we just redeclare interface User down here with name string. Now, if we say const user is a User here, then what this is going to do is it's going to require id and name.
3:03 This is what's called declaration merging. If I give this a different name, then it's only going to require id from name, because interfaces with the same name in the same scope get merged. What? Really complicated. This doesn't happen with types too.
3:20 If you say type User is this, then it's actually going to yell at you, because you've got a duplicate identifier, but interfaces, especially when they're inside the global scope, can be used to patch together different things which all need to add into the global scope.
3:39 That's what it's called, declaration merging. That's why this behavior happens, is because we need to stuff things into the same object.
3:48 The way we do it and the way we get back to this kind of window.makeGreetingSolution is we say we have our interface Window here, which we know it's an uppercase Window, because it's expressed here, this Window here. We can add makeGreetingSolution to that interface. Let's say returns a string.
4:13 This is really cool, because it means that it doesn't get put on the globalThis. It gets stuffed just into the Window interface. What it means is you just get this really nice...If you have something on the window that isn't being expressed anywhere, this is the way to do it.
4:31 Hopefully, that makes sense. You're inside a global ambient context, just like a .d.ts file, you're declaration merging with the Window interface, and you're adding a function to it.