Introduction to TypeScript Decorators
In this guide you'll learn how to work with TypeScript decorators with a basic code example that examines the core functionality.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

Welcome to the section on decorator's in typescript in this section we're going to go into the topic of decorator's and talk about a few of the different variations that typescript allows us to work with and decorators are going to be a very very common thing that you're going to be using if you're going to be taking your typescript knowledge and applying it to angular 2.

So the first thing you need to do in order to get decorator's working is we need to make three changes to our TS config file here. First thing I did is I commented out the target ES6 and I changed it. So we're going to target ES5. And then I added two lines of code and you can go to the source code that goes along with the course and you can access the first one is experimental decorators. I set that to true and then emit decorator metadata and I set that to true. And that is all you'll need to get decorator's working on your system.

large

So let's get into what decorator's actually are. I'm going to in this guide give you a very basic concept of what decorators are at a high level and we're going to talk about a dead simple example and then in the next few guides, We'll get into some more practical things.

So very first thing I'm going to do is I'm going to create a class and this class is going to be called Post and inside of the class. I'm just going to say that we have some function. It doesn't really matter what the function is and it doesn't matter what the function is going to do. But let's pretend that we have this function and we want to have some other processes be associated with it, but for the purposes of things like code reuse and having the dry principle which is Do not repeat yourself then we want to have some code that in the best term it decorates this function. In other words, it goes in and it's like you're layering functionality on top of an on top of other components in your codebase. It's not just for functions We'll get into how you can do this for classes as well. And what you can do is you can come down and create some other functions. So decorators at their core are just functions so you know how to create these. And I'm going to create regular function declarations and all create one that says "Process 1". Now inside of this, I'm going to start off by giving a console log so you can see when these processes occur.

class Post {
  someFunction() {}
}

So I'm going to say process one has run probably be a good idea to put it in a string. And now a very important thing to know when it comes to working with decorator's is that a decorator has to return a function. So we're looking at a dead simple example. So I'm just going to return a regular function and this next thing I'm going to do. Is going to look a little bit weird and it probably won't make sense at all. I don't want you to get confused or feel overwhelmed by this next item. This is going to be what we're passing into this function that's getting a return. So simply if you're following along simply follow along. Enter these items in and then discuss what they are and then I'll also let you know that what's going inside of here is not the most critical thing for you to know. It simply is what typescript requires in order to consider this process this function to be a decorator.

So inside we're going to pass in a target and we're going to pass in what's called a property key. This is going to be of type string. And lastly, we're going to pass in a descriptor. This is going to be of property descriptor. That's it. Now inside of here, I'm going to console log and I'm going to say "process one has been called".

function processOne() {
  console.log("processOne has run");
  return function (target, propertyKey : string, descriptor : PropertyDescriptor) {
    console.log("processOne has been called");
  }
}

So what exactly is going on here? We have a function, and in order to make this a decorator and you can just make any kind of function a decorator typescript is way too picky for that. When you create a function if you want it to work like a decorator then you're half you have to follow the decorator rules. So what this is expecting is a target and then it's expecting a property key and then a descriptor. All of these items are just specific to what the typescript compiler needs in order to make this possible. So I would really do not worry about this remember the goal of this course is for you to become aware of all the different components that you'll need in order to build Angular 2 applications. In angular 2. You may create some decorator's every once in a while but more than anything what you'll do is you'll actually take the decorator's that angular has created and you'll implement them. So you're not going to really spend all day writing things like Target property key and descriptor what you're going to be doing is you are going to be taking decorator's that's angular too has created. And then you'll be implementing those so you'll be adding that to your code. So I really. Because decorators can be very complex and they can have a lot of different requirements. I almost considered leaving this section of the course out entirely because I didn't want it to confuse you especially because a lot of the things are going to be talking about are things that usually when you're building angular 2 apps you're not going need. However, I thought about it a lot and I decided that it was important to have a section dedicated just to decorators. And the reason for that is something we're going to take a look at in the next guides which is if you look through an angular 2 applications code base you are going to see decorators all over the place. You're going to be using them almost in every code file. And the reason for that is because Google and the angular 2 team decided that it was critical to be able to have this kind of functionality for a modern framework and you'll see as we get into some of the other things we're going to be building and some of the other case studies for decorator's how powerful they can be.

However, I don't want you to get you know scared and intimidated by seeing code like this because a lot of this is code they are really not going to be using on a day to day basis when you're working with angular unless you're you know you're building your own decorators. By that time you probably will be way more experience with typescript in this type of code won't scare you away. So I want you to focus on the processes that are occurring and the code flow and the syntax for how these things are being called and then later on you can get deeper into how you how to create these from scratch.

So I'm going to copy this and I want to create another process so I'm going to create process two. And paste this for a console log statements and this is literally all we need to do for the code. So as far as these two functions go. So I have these two functions. They're both decorators. Now in order to add these I'm going to come into the class and write above the function that I'm wanting to decorate. I'm going to use an at symbol and then I'll say the name of the the name of the decorator I'm wanting to call and then I'm also going to show you that you can do multiple decorators at the same time. So what I'm doing here is I'm decorating this some function I'm taking everything and process one and everything and process two and you'll see that by using this decorator pattern it is going to apply these functions and it's going to run these when the code is processed. Now this is another really critical thing that I'm going to show you when we actually run the code.

class Post {
  @processOne()
  @processTwo()
  someFunction() {}
}

function processOne() {
  console.log("processOne has run");
  return function (target, propertyKey : string, descriptor : PropertyDescriptor) {
    console.log("processOne has been called");
  }
}

function processTwo() {
  console.log("processTwo has run");
  return function (target, propertyKey : string, descriptor : PropertyDescriptor) {
    console.log("processTwo has been called");
  }
}

So if I run tsc. And then node 030_decorator_introduction. You can see that this all gets processed. However if you look back in the code you may notice something that's a little bit weird. Do you notice that we never actually instantiated this post. We never did something like var post equals new Post and you know did a thing like that and that's something that's very critical to understand with the way that decorator's work is they are called at runtime, not an object instantiation. So that's something that is really important to know. And once I saw that and I saw this example I thought OK that actually makes way more sense we're not really decorator's aren't really as focused as much on the objects as much as they are on the behavior in the code of the class. So this class is structured in a way where it's very basic only has one function and it doesn't even have any behavior inside of it. But what we were able to do is we were able to essentially layer on other functions and those were run when we ran the code. Even though we never actually created a post object. Now if we created a post object nothing would change. These would get run the exact same way that they got run before.

So if you want to think about a case study for how this could work in a real world scenario let's pretend that I'm going to copy this and say real post. Now I'm not going to decorator's I'm just going to have one, and let's say this is a delete function. So this is the ability to delete a post in an application. You might have a decorator such as. Admin and then this obviously won't run because you'd need a admin function but this is kind of a good example of how this could be used in a real world scenario is you can use decorators to verify if a user has access to perform a certain things so decorators are very popular when you're building things like permissions structures or anything like that because what you're able to do is you're able instead of filling up your delete method here with all kinds of checks to see. OK is this person authorized and you know you have all of this code that really clutters up the code base instead of doing all that you can just pass in this admin decorator and it can perform.

class RealPost {
  @admin)
  delete() {}
}

All of that code so it can inside of this function:

function processTwo() {
  console.log("processTwo has run");
  return function (target, propertyKey : string, descriptor : PropertyDescriptor) {
    console.log("processTwo has been called");
  }
}

You would have something that looks kind of like this and it could say OK if this user is an authorized user then you know we're going to give these kind of permissions and we set it all up that way as opposed to loading up your method and having a really messy delete method. So a lot of the things that decorators are used for are for best practices and being able to slide to helper modules in and be able to call those dynamically without cluttering up your entire code base. So that's kind of a practical reason why they're used.

Last thing I'm going to say in this introduction to decorator guide is let's come back and see the output here. Now this is important is to know the order of things. So we had four console log statements and notice though the difference on when they're called. Process 1 has run process to has run then process two is called process one has been called. So let me copy this. Just so we can see it alongside our code. Make it a comment and I'll leave this in here for you so when you go to when you go to the source code you can see the exact output of this entire class. So let's take a look at this process one is run. This one makes sense. This is the very first thing that happened is our process one decorator was called and this console log statement happened. However the next thing was not sequential. This process one has been called is not the next one that happened if you come down and look you can see process two has run. So we have a process two has run. That's the next thing that happened.

class Post {
  @processOne()
  @processTwo()
  someFunction() {}
}

function processOne() {
  console.log("processOne has run");
  return function (target, propertyKey : string, descriptor : PropertyDescriptor) {
    console.log("processOne has been called");
  }
}

function processTwo() {
  console.log("processTwo has run");
  return function (target, propertyKey : string, descriptor : PropertyDescriptor) {
    console.log("processTwo has been called");
  }
}

So if you want to look at kind of the order of when these items are called and when they're run you can see that the functions process one and process two these were both run right away. However the values that were in the return statement were not run right away. And this is also kind of interesting and you may think okay that makes sense. You know the return statements are going to be the last things to occur. And I ran everything above those first. However it's also a little bit tricky because notice how it says Process two has been called. So it didn't go you know in a full sequential circle it went process one. Process to then it took the return statement from process two and then it went to process one. So that's something that's really important is you know whenever you're using decorators is to understand the the order of when these are going to be called. One thing I can say is there are a ton of circumstances where you're going to be calling multiple decorators on the same function like we're doing right now. So you may not have to deal with this a ton. However there have been times where I have needed to do that and I ran into a few little bugs where I thought one thing would happen before the other one. It was because I didn't realize the order occurred exactly like this where it actually went to the second return statement that was called and then went back up to finish this last one.

So that is it a dead simple explanation of how decorator's work and the it's called the decorator design pattern on how that works in typescript and in the next few guides we're going to go through some other more practical examples of how you can use decorators to build out typescript programs.

Resources