Introduction to Closures in TypeScript
This guide examines how to work with TypeScript closures, including three examples of how closures work, specifically discussing the idea of variable scope.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

So we've talked a lot about some more advanced functionality when it comes to typescript, such as the difference between declarations and expressions and how to work with immediately invoked functions. And now I think we're ready to talk about closures, so closures are one of the most widely used things that you're going to come across when building out angular applications. And so they're used extensively in typescript and then once you get into the angular side of the world you're going to find that they are used all over the place there and there's very good reason for it and that reason really is that a closure is a powerful tool for being able to capsule late data and behavior. If you are familiar with other languages other object oriented languages like a java or a ruby then you'll be happy to know that a lot of that understanding can also be translated into understanding. Closures in the JavaScript side of the world.

JavaScript at its core started by not having classes. JavaScript wasn't a true object oriented language at all. Especially in the beginning. It's evolved over the years to get closer to that, but in the very beginning JavaScript didn't have any concept of being able to create an object kind of definition like classes or anything like that. So people had to create work arounds for that. And closures were one of the things they built in. Closures have the ability to encapsulate all kinds of behavior and data all into the same object. However that's only the start of what closures do. And that's all we're going to walk through is getting a better feel for exactly what closures are and the best way to do it is to work with examples. First and foremost I want you to know that all functions in JavaScript are closures. So technically we've been working with closures throughout this entire course.

Example 1

However now we're going to go into some of the power that you can extract out of closures. I'm going to start with two contrived examples to show you the syntax and then I'm going to go through a more practical one at the end. The first one is I want to show that functions have access to any public variables in the outer scope. I'll show you what that means and I'm writing these as comments so that when you have the source code you'll be able to reference these however you need. I’m going to create a very basic function. It's going to be more basic than our full name and we're just going to call it a name function and inside of this it's going to be a string and it's going to return void. Remember that returning void means that it doesn't actually return a value. It's simply going to do something like log something like we're going to use the console log for it. So here I'm going to create a variable called n it's going to be of type string and it is going to be equal to name.

Like I said this will give me a contrived example so that I can break it down into a very basic format. Technically you would not have to do this because you'd be able to pass your name however you wanted to something else. From here I’m going to create another function. So I'm creating a function inside of a function which is a very important thing that is needed for closures. You will see that closures have functions inside of them so they can nest functions. And here I'm going to create a variable called print name and inside of it, it's not going to return anything. So it's going to be empty. And inside of this it's going to be dead simple it's just going to say console,log. And it's going to take the N variable so it's going to take this variable that we created right here. And it's simply going to print it out. And now the last thing I'm going to do is call print name as a function and now this should work. So now if I call name function. Pass in an argument of just my name it should print out. And there you go. So that worked.

function nameFunction(name: string) : void {
  var n : string = name;

  function printName() {
    console.log(n);
  }

  printName();
}

nameFunction(‘Jordan’);

Now if you're wondering why I wanted to illustrate this it's because I want to show that right here we have a variable then we have a function. And because of how closures work this variable because it's in the outer scope is available here to the function. So this is available and this is where the concept of closures comes from because it closes in and encapsulates all of the data in the outer scope and it's all available here. And we can call it later. And this is referencing the variable that's actually outside the function. So that is the first way of doing it actually going to take this a step further to comment out all this code.

Example 2

And now I want to talk about how the inner functions maintain access to the outer scope even after the values are returned and exclamation mark because that's actually a pretty big deal. I'm going to update our function keep our variable here. I'm going to get rid of all this and I'm going to say and I'm also going to get rid of our return void here and because I'm going to return a function. Notice how I'm just returning a function by itself. I'm not returning a named function or anything like that. I'm just returning a function and here I'm going to say console log n and then coming down here for the named function I actually go store this in a variable save var and call it name let's call it name again set it equal to the named function. And now I can call name again though we're going to call it as a function and for just so you know that this is different. I'm going to change the name of it. It I'm going to run this code.

function nameFunction(name: string) : void {
  var n : string = name;

  return function() {
    console.log(n);
  }
}

var nameAgain = nameFunction(‘Tiffany’);
nameAgain();

And as you can see that prints out here is still not impressed. I want you to actually take a look here because this is a pretty big deal. And when we get into more challenging examples and it's become apparent why it's a big deal. But the main reason is that this value and I put Tiffany inside here as the argument to the function and then this was stored here when we call them name again. This is actually still able to access these values even though the functions returned. And even though it's done the name again still has access to it because it was stored in a variable and because it's a closure and an encapsulated not just a behavior but it encapsulated the data that was there. Now if you're like me first time I saw some examples like that. I still wasn't very impressed. So if that doesn't seem like a big deal to you you're in good company because I was really the same exact way. Now here I'm going to go into a practical example of how this could be used.

Example 3

Let's imagine that you work for a baseball stadium and you are the one in charge of building the methods and the behavior for the jumbotron and you have been asked to know where every player in the line up is. So if they're the first batter you want the number one coming out second batter you want the next batter coming out and you could write some code that would make it pretty easy to do that. However we're going to make it more challenging because say that your boss told you that you can't just write a code snippet that works. You also have to write a code snippet that can be called from anywhere else so you're going to have to encapsulate all of your data and all your behavior into a single object or a single set of methods that can be called anywhere else in applications so it can show up on the jumbotron.

So for that we're going to create a closure and we're going to call it lineup. So in lineup we've created a lineup card before. I'm going to create one called lineup and inside of this and I'm going to declare a variable. Now this variable is called now batting. It's going to be of type number and we'll set it equal to 1 for starting off. So this is going to be like the beginning of the game. Now instead of creating another function or anything like that we're actually going to return an object. So here I'm going to say return and then curly braces and this is going to return a JavaScript object so that we can put anything inside of this we could return data. But in our case we're actually going to return a couple different methods. First method is going to be called next batter and next batter is not going to take any parameters. And the reason it's not going to take any parameters is because it doesn't have to because it has access to this now batting variable right here. So we have this now batting variable but because this is JavaScript and because this is a closure we have access to this variable right here and we can increment it and because this is a JavaScript object we put a comma at the end. And now we also want to know who the current batter is. So I’m going to say this is a current batter. This is going to be a function as well. And if you're wondering why I'm putting these on one line it's because it's just easier. I could if there were multiple lines I could easily go just like this and add the code I want there but that is there's not really need for it because these are very basic.

Now because now batting simply increments the next batter method here it only increments in our batting variable so we don't have to return anything. But to find the current batter we do need to return something so I'm going to say now batting and that is all we have to do for this closure. So let's walk it really quick. We created a function here takes no arguments and we're saying that we have a variable piece of data inside of it called. Now batting it set to 1. And what this function's going to do is it's going to return multiple items so it's going to have the ability to return next batter or the current batter. This is just a function that is going to increment this variable here. Current batter is actually going to retrieve it for us. So what we're going to do is create a variable called batters set it equal to the lineup. Now we can actually work with the data. So I’m going to create some console log statements batters and it shows that we have access to current batter and because it's a function we have to pass in parentheses afterwards. And now below this I'm going to copy a few other ones. So what I want to happen is I want to have the current batter method called first. This is going to go in the closure. It's going to see all of this data and it's going to return the current batter function which in turn returns the value of now batting. So all it's doing if you go back to our example on your heading up this Jumbotron print out Project right here you're just saying I want to know what this value is. And so it's pretty straightforward. We have current batter. But knowing that is fine but it's kind of pointless if we don't increment it. So right after you press current batter let's say that batter gets a hit or gets out. Then you want to increment it. So now this next line you're going to call next batter next batter is not going to print anything out. So we don't need console log statements. It simply is going to increment this value by one. After that we want to know who the batter after that is. So it's going to print out who the current batter is after that, and then we want to increment it again. And this obviously would go on for the entire game but let's see if this actually works. So I'm going to come the terminal tsc and let's run it and you can see that that worked perfectly.

function lineup() {
  var nowBatting : number = 1;

  return {
    nextBatter() { nowBatting++},
    currentBatter() { return nowBatting}
  }
}

let batters = lineup();

console.log(batters.currentBatters());
batters.nextBatter();
console.log(batters.currentBatter());
batters.nextBatter();
console.log(batters.currentBatter());

We have the values 1 2 and 3. Now let's just sit back for a second and think about how powerful this is because if you are thinking oh well a loop could have done that. Yes for this very basic example a loop could do that. However what we've done here is we've actually made this batter's variable a living breathing object so that even after we've called it five times it's maintained at state. So we have changed the batter twice and we've had the ability to call this batter three times. So even though the process ran when we called lineup and it returned these items we were able to go back in and make changes just by calling these methods right here.

Now this is really powerful for a number of reasons. Let's imagine that you are using a closure in an angular program where you want to be able to go retrieve posts from an API. That's something that's very common and that's exactly what we're going to do when we get into angular development is we're going to call a API via json and we're going to use a closure and we're going to have a line of code very much like this one right here. So we're going to call the API. It's going to bring back a number of objects but it's no real point in just having those objects without having behavior inside of that. So here we'll maintain the ability to maintain the connection to that API and then we can do things like make change to a post or we can retrieve the user details of a post. There's all different kinds of things we can do. And by using a closure that gives us that real world object and gives us the ability to have all of the data and the behavior all in one piece.

Now if you think that this sounds very similar to the way that classes work in other languages. You're absolutely right. Closures were one of the big things that were created because JavaScript didn't have classes. However in addition to being able to encapsulate data and behavior closures also give us the ability to maintain specific levels of state. And it does that by keeping the connection in keeping the scope with the data inside of here. So when we created this lineup and we stored it inside the batter's variable by doing this we now maintain a direct connection between this item and the other one. So if I come here and say pitchers and print out what the current pitcher is. Let's see what this does.

function lineup() {
  var nowBatting : number = 1;

  return {
    nextBatter() { nowBatting++},
    currentBatter() { return nowBatting}
  }
}

let batters = lineup();

console.log(batters.currentBatters());
batters.nextBatter();
console.log(batters.currentBatter());
batters.nextBatter();
console.log(batters.currentBatter());

let pitchers = lineup();
console.log(pitchers.currentBatter());

This will be the last one on the list. You can see that it goes back to one and that's perfect because what we have proved by doing that is that batters is its own real world object. You can imagine this as something that you would build just like I gave the example for something you'd build for the baseball stadium. And whenever you have the batters like that it would work. A good example would be it taking that as a case study you could have. Team A. Here, and this keeps track of those batters and then you could have team B over here and you'd keep track of theirs. And you could maintain a direct connection to each one of those scopes and be able to go down the line in it without having to you know do something like use loops or something that really wouldn't work for this kind of a use case.

So part of the reason why is has been a longer episode and why I've really stressed the importance of this is because once we get into more advanced type scrap and eventually if you take your typescript knowledge and apply it to angular you are going to see methods like this and processes like this all day long. If you go and just look at some example angular 2 projects it is literally filled with code that looks exactly like this. So I want to stress the syntax the importance and also what's going on in the background here and show you that all of the things that are occurring that make this type of behavior possible. And it's all possible by using closures.

Resources