Check out a free preview of the full Intermediate TypeScript course:
The "Declaration Merging" Lesson is part of the full, Intermediate TypeScript course featured in this preview video. Here's what you'd learn in this lesson:

Mike discusses stacking types and values on an identifier, how to understand what is attached to an identifier, and what a namespace is. A walk through of applying type and value tests to a class and how classes relate to declaration merging is also provided in this segment.

Get Unlimited Access Now

Transcript from the "Declaration Merging" Lesson

[00:00:00]
>> The first topic we're going to discuss today is declaration merging. And this is the phenomenon by which types, and values can piggyback on top of each other. And they can be treated as a single named entity in your source code. By the end of this chapter, you'll be able to look at tooltips in Visual Studio code, which is the same tooltips that we see on this website.

[00:00:24] And you'll be able to make sense of exactly what's going on. Am I working with the type? Am I working with the value? How can I tell the difference? In this example, we have two what I'm going to call identifiers. And what I mean by an identifier is just a named thing that's defined in a single place.

[00:00:45] So we have one identifier called Fruit, which is a type. We know that, because it's an interface. And here is an identifier called banana, which is a value. TypeScript allows us to stack multiple things onto a single identifier. In this situation, we can see that we've named our interface, and our const variable the same thing, right?

[00:01:08] They both fruit both with the capital letter. And we can see when we look at the tooltip for the interface, it's clearly just an interface. When we look at our variable, clearly it's just a concept declaration. But look at what happens when we attempt to export this as if we were exposing it outside of this TypeScript module.

[00:01:28] We can see this tooltip contains both, there's our const declaration, and there's our interface. These are actually two things stacking on top of the same word, Fruit. There's actually a third thing that we can stack onto this tooltip, so to speak unto this word, and that is a namespace.

[00:01:51] So in this example, we can create a class called Fruit with a static function on it. Now, we can create a namespace. And you can think of this as serving a very similar purpose as static class functions. And then finally we have a type called Fruit. So, if we hover over this export, and look at the tooltip, now we have three things class, interface, and namespace.

[00:02:18] So this is three things in one. Now you may be wondering when I'm importing stuff from other dependencies, how do I know what I'm working with? Well, I'm going to give you some investigative tools that you can use. To probe around, and to figure out what exactly is on one of these identifiers, right?

[00:02:42] If I'm given Fruit, how can I test whether it's a class, sorry, how can I test whether it's a value, or a type, or both, or maybe neither? So, here are our tools. First, let's begin by creating a value, a type, and a namespace. And we've given them obvious names, so that when I put them up the screen here, you can remember what's going on, right?

[00:03:10] So we've clearly got a value here. We've clearly got a type, which is just an empty interface. And then here we have a namespace. So you know something is a value if in a let or const declaration like this, you can put it on the right-hand side of the equal sign, and you get no error.

[00:03:33] If we try to do the same thing with the type, like we're doing down here, you can see that the compiler is not happy with us. And it's telling us is a type only refers to a type, but we're trying to use it as a value. So this alerts us to the fact that this is purely a type, there is no value part to this identifier.

[00:03:58] Let's look at a similar test but four types. So if we try to put this on the left-hand side of this assignment operator, and we get no error. That's an indication that this identifier can be treated as a type. And look what happens down here, if we try to do the same thing with a value, we get an error message.

[00:04:20] The thing we're using, it refers to a value, but we're trying to use it as a type here. So that failure tells us there's no type piece to whatever is on this thing, which is purely a value. Now, there's no equivalent test for namespaces, although the tooltips make it fairly obvious what's going on.

[00:04:45] So you see the word namespace on the left of the tooltip, that is a clear indicator. So you'd have to hover over it in order to figure out what exactly is happening there. While we're talking about namespace, some of you may be wondering, what's the point of these things?

[00:05:01] And maybe you haven't seen them before, and you're wondering why they're even part of TypeScript? Well, an important aspect of TypeScript is it needs to be able to describe existing JavaScript libraries. Just think about when TypeScript was first made, and when they were trying to gain adoption. They needed to be able to say, here's type information that matches this existing library, which is written in regular JavaScript.

[00:05:31] So they needed to be able to type things like jQuery. And if we go to the jQuery homepage here, we can see some example usage right on the front page. So if we look down we can see, all right? Sometimes we're invoking this dollar sign thing as a function.

[00:05:48] And then down here, it appears that we're grabbing things off of the dollar sign, and they themselves are functions. So let's copy that code over, right? So this is basically the same code, it represents the same code. And we need to be able to describe types that can allow us to do these kinds of things.

[00:06:13] Here's how we might accomplish that. You can see here we're defining a function whose name is dollar sign. And we pass a selector in this could be up here, like h1 tags with a class title. And we're going to get all matching elements in a node list, as return value.

[00:06:33] In addition to that, to support this use case here, with .ajax, we need dollar sign to be a namespace that kind of stacks on top of that function of the same name. And that's what's allowing us up here to invoke this directly. And to use some of these functions that are sort of dangling off of the main function here.

[00:06:58] That's where namespaces are useful. Now, writing a library in this way. It's sort of leftover from the old way of consuming JavaScript dependencies, where you'd add a script tag to your app. And you would find that when all of your scripts have loaded, there's this global variable that has been tacked on to window or something, dollar sign, right?

[00:07:26] And you could grab that, and basically access all of the capabilities of a library through that global variable. We don't really write things that way in modern JavaScript code bases. Now that we have modules, and better ability to consume third party code in that way. But we still need to be able to describe types for some of that old stuff that's lying around.

[00:07:49] So my advice is don't use namespaces too much. Don't focus on them too much. Cuz they're really about backwards compatibility, and not something that you often find is added to modern code bases. So now that we have a basic understanding of declaration merging, and how we can sort of sandwich or stack multiple things on a single named identifier.

[00:08:19] I want us to take a look back at the way classes work. So let's say that we have a class, like what you see on the screen now. It has three fields, each of which is optional, name, mass, and color. And then, it has a static functions. So, you could think of this, as a kind of a backup constructor of some sort.

[00:08:42] Sometimes you find that that's a good way to use static functions, a good use case for them rather. So if this is our class, let's apply the tests that we just discussed, and identify whether this is a value, or whether this is a type. So first we can test for value.

[00:09:02] And our test is to put it on the right-hand side of the assignment operator, and see if we get an error, and we don't. That means Fruit is a value. And when we take a look at this value. We can see that there's this createbanana thing, but note that we do not see color in this list.

[00:09:21] The reason is the value here is the class itself, not an instance of Fruit, it's the the factory or the constructor, right? We're seeing createbanana. We're seeing call, which indicates that this is kind of a function like classes or functions. So we would call this the static side of the class.

[00:09:46] Now, if we take that exact same identifier, and we test whether it's a type, the test is positive. The Fruit identifier is also a type. And when we look at the available completions here for the letter c, we can see that color is the only thing that's showing up.

[00:10:07] That's because when used as a type, this class is an interface that describes an instance of the class. So if you've been using classes, all along, now you know that they are a value, and a type of the same name stacked on top of each other. And depending on the context in which you use it, whether it's in the value position over here, or the type position over here.

[00:10:35] You're using one piece of it, or the other. So you've been making use of declaration merging, all along.