# \[TYPESCRIPT] Một số scripts hay được sử dụng trong typescripts (ok)

### Một ví dụ sử dụng decorators

```
function delay() {
	console.log("delay(): evaluated");
	return function(target,propertyKey: string, description: PropertyDescription) {
		console.log("delay(): called");
	}
}
function other() {
	console.log("other(): evaluated");
	return function(target,propertyKey: string, description: PropertyDescription) {
		console.log("other(): called");
	}
}
class TimeOut {
	count: number = 1;
	@delay();
	@other();
	callMe() {
		this.count ++;
		console.log('callMe is been called with', this.count);
	}
}
let timeout = new TimeOut();
console.log('going to call this method callMe');
timeout.callMe();
console.log('after callMe is been called', timeout.count);
```

![](https://2726517656-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M1E4Gk2ppVKb4olmnun%2F-MM48tnomIfwfXRR1Qkh%2F-MM49bbVm4AWtI_wxdDE%2FScreenshot_1.png?alt=media\&token=468958a3-c317-4c63-885a-e25111683ecf)

Chú ý tới lệnh&#x20;

```
node .
```

[https://www.youtube.com/watch?v=cRWF9e-1DxQ](https://www.youtube.com/watch?v=cRWF9e-1DxQ)

```
{
  "scripts": {
    "build": "tsc",
    "start": "ts-node index.ts"
  }
}
```

## A practical guide to TypeScript decorators

###

May 13, 2020  5 min read ![A Practical Guide to TypeScript Decorators](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/05/a-practical-guide-to-typescript-decorators.png?fit=730%2C487\&ssl=1)

We can all agree that JavaScript is an amazing programming language that allows you to build apps on almost any platform. Although it comes with its own fair share of drawbacks, TypeScript has done a great job of covering up some gaps inherent in JavaScript. Not only does it add type safety to a dynamic language, but it also comes with some cool features that don’t exist yet in JavaScript, such as decorators.

### What are decorators? <a href="#whataredecorators" id="whataredecorators"></a>

Although the definition might vary for different programming languages, the reason why decorators exist is pretty much the same across the board. In a nutshell, a decorator is a pattern in programming in which you wrap something to change its behavior.

In JavaScript, this feature is currently at [stage two](https://github.com/tc39/proposal-decorators). It’s not yet available in browsers or Node.js, but you can test it out by using compilers like Babel. Having said that, it’s not exactly a brand new thing; several programming languages, such as Python, Java, and C#, adopted this pattern before JavaScript.

Even though JavaScript already has this feature proposed, TypeScript’s decorator feature is different in a few significant ways. Since TypeScript is a strongly typed language, you can access some additional information associated with your data types to do some cool stuff, such as runtime type-assertion and dependency injection.

### Getting started <a href="#gettingstarted" id="gettingstarted"></a>

Start by creating a blank Node.js project.

```
$ mkdir typescript-decorators
$ cd typescript decorators
$ npm init -y
```

Next, install TypeScript as a development dependency.

```
$ npm install -D typescript @types/node
```

The `@types/node` package contains the Node.js type definitions for TypeScript. We need this package to access some Node.js standard libraries.

Add an npm script in the `package.json` file to compile your TypeScript code.

```
{
  // ...
  "scripts": {
    "build": "tsc"
  }
}
```

TypeScript has labeled this feature as experimental. Nonetheless, it’s stable enough to use in production. In fact, the open source community has been using it for quite a while.

To activate the feature, you’ll need to make some adjustments to your `tsconfig.json` file.

```
{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true
  }
}
```

Create a simple TypeScript file to test it out.

```
console.log("Hello, world!");


$ npm run build
$ node index.js
Hello, world!
```

Instead of repeating this command over and over, you can simplify the compilation and execution process by using a package called `ts-node`. It’s a community package that enables you to run TypeScript code directly without first compiling it.

Let’s install it as a development dependency.

```
$ npm install -D ts-node
```

Next, add a `start` script to the `package.json` file.

```
{
  "scripts": {
    "build": "tsc",
    "start": "ts-node index.ts"
  }
}
```

Simply run `npm start` to run your code.

```
$ npm start
Hello, world!
```

For reference, I have all the source code on this article published on my [GitHub](https://github.com/rahmanfadhil/typescript-decorators). You can clone it onto your computer using the command below.

```
$ git clone https://github.com/rahmanfadhil/typescript-decorators.git
```

### Types of decorators <a href="#typesofdecorators" id="typesofdecorators"></a>

In TypeScript, decorators are functions that can be attached to classes and their members, such as methods and properties. Let’s look at some examples.

#### Class decorator

When you attach a function to a class as a decorator, you’ll receive the class constructor as the first parameter.

```
const classDecorator = (target: Function) => {
  // do something with your class
}

@classDecorator
class Rocket {}
```

If you want to override the properties within the class, you can return a new class that extends its constructor and set the properties.

```
const addFuelToRocket = (target: Function) => {
  return class extends target {
    fuel = 100
  }
}

@addFuelToRocket
class Rocket {}
```

Now your `Rocket` class will have a `fuel` property with a default value of `100`.

```
const rocket = new Rocket()
console.log(rocket.fuel) // 100
```

#### Method decorator

Another good place to attach a decorator is the class method. Here, you’re getting three parameters in your function: `target`, `propertyKey`, and `descriptor`.

```
const myDecorator = (target: Object, propertyKey: string, descriptor: PropertyDescriptor) =>  {
  // do something with your method
}

class Rocket {
  @myDecorator
  launch() {
    console.log("Launching rocket in 3... 2... 1... ")
  }
}
```

The first parameter contains the class where this method lives, which, in this case, is the `Rocket` class. The second parameter contains your method name in string format, and the last parameter is the property descriptor, a set of information that defines a property behavior. This can be used to observe, modify, or replace a method definition.

The method decorator can be very useful if you want to extend the functionality of your method, which we’ll cover later.

#### Property decorator

Just like the method decorator, you’ll get the `target` and `propertyKey` parameter. The only difference is that you don’t get the property descriptor.

```
const propertyDecorator = (target: Object, propertyKey: string) => {
  // do something with your property
}
```

There are several other places to attach your decorators in TypeScript, but that’s beyond the scope of this article. If you’re curious, you can read more about it in the [TypeScript docs](https://paper.dropbox.com/doc/A-practical-guide-to-TypeScript-decorators--Az3eBblNHj5Doz3Z4NyijRAuAQ-7CB7irprCVgmPH1mS0mY4).

### Use cases for TypeScript decorators <a href="#usecasesfortypescriptdecorators" id="usecasesfortypescriptdecorators"></a>

Now that we’ve covered what decorators are and how to use them properly, let’s take a look at some specific problems decorators can help us solve.

#### **Calculate** **e**xecution **t**ime

Let’s say you want to estimate how long it takes to run a function as a way to gauge your application performance. You can create a decorator to calculate the execution time of a method and print it on the console.

```
class Rocket {
  @measure
  launch() {
    console.log("Launching in 3... 2... 1... ");
  }
}
```

The `Rocket` class has a `launch` method inside of it. To measure the execution time of the `launch` method, you can attach the `measure` decorator.

```
import { performance } from "perf_hooks";

const measure = (
  target: Object,
  propertyKey: string,
  descriptor: PropertyDescriptor
) => {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const finish = performance.now();
    console.log(`Execution time: ${finish - start} milliseconds`);
    return result;
  };

  return descriptor;
};
```

As you can see, the `measure` decorator replaces the original method with a new one that enables it to calculate the execution time of the original method and log it to the console.

To calculate the execution time, we’ll use the [Performance Hooks API](https://nodejs.org/api/perf_hooks.html) from the Node.js standard library.

Instantiate a new `Rocket` instance and call the `launch` method.

```
const rocket = new Rocket();
rocket.launch();
```

You’ll get the following result.

```
Launching in 3... 2... 1... 
Execution time: 1.0407989993691444 milliseconds
```

#### Decorator factory

To configure your decorators to act differently in a certain scenario, you can use a concept called decorator factory.

Decorator factory is a function that returns a decorator. This enables you to customize the behavior of your decorators by passing some parameters in the factory.

Take a look at the example below.

```
const changeValue = (value) => (target: Object, propertyKey: string) => {
  Object.defineProperty(target, propertyKey, { value });
};
```

The `changeValue` function returns a decorator that change the value of the property based on the value passed from your factory.

```
class Rocket {
  @changeValue(100)
  fuel = 50
}

const rocket = new Rocket()
console.log(rocket.fuel) // 100
```

Now, if you bind your decorator factory to the `fuel` property, the value will be `100`.

#### Automatic error guard

Let’s implement what we’ve learned to solve a real-world problem.

```
class Rocket {
  fuel = 50;

  launchToMars() {
    console.log("Launching to Mars in 3... 2... 1... ");
  }
}
```

Let’s say you have a `Rocket` class that has a `launchToMars` method. To launch a rocket to Mars, the fuel level must be above 100.

Let’s create the decorator for it.

```
const minimumFuel = (fuel: number) => (
  target: Object,
  propertyKey: string,
  descriptor: PropertyDescriptor
) => {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args) {
    if (this.fuel > fuel) {
      originalMethod.apply(this, args);
    } else {
      console.log("Not enough fuel!");
    }
  };

  return descriptor;
}; 
```

The `minumumFuel` is a factory decorator. It takes the `fuel` parameter, which indicates how much fuel is needed to launch a particular rocket.

To check the fuel condition, wrap the original method with a new method, just like in the previous use case.

Now you can plug your decorator to the `launchToMars` method and set the minimum fuel level.

```
class Rocket {
  fuel = 50;

  @minimumFuel(100)
  launchToMars() {
    console.log("Launching to Mars in 3... 2... 1... ");
  }
}
```

Now if you invoke the `launchToMars` method, it won’t launch the rocket to Mars because the current fuel level is 50.

```
const rocket = new Rocket()
rocket.launchToMars()


Not enough fuel!
```

The cool thing about this decorator is that you can apply the same logic into a different method without rewriting the whole if-else statement.

Let’s say you want to make a new method to launch the rocket to the moon. To do that, the fuel level must be above 25.

Repeat the same code and change the parameter.

```
class Rocket {
  fuel = 50;

  @minimumFuel(100)
  launchToMars() {
    console.log("Launching to Mars in 3... 2... 1... ");
  }

  @minimumFuel(25)
  launchToMoon() {
    console.log("Launching to Moon in 3... 2... 1... ")
  }
}
```

Now, this rocket can be launched to the moon.

```
const rocket = new Rocket()
rocket.launchToMoon()


Launching to Moon in 3... 2... 1... 
```

This type of decorator can be very useful for authentication and authorization purposes, such as checking whether a user is allowed to access some private data or not.

### Conclusion <a href="#conclusion" id="conclusion"></a>

It’s true that, in some scenarios, it’s not necessary to make your own decorators. Many TypeScript libraries/frameworks out there, such as [TypeORM](https://typeorm.io/) and [Angular](https://angular.io/), already provide all the decorators you need. But it’s always worth the extra effort to understand what’s going on under the hood, and it might even inspire you to build your own TypeScript framework.

### [LogRocket](https://logrocket.com/signup/): Debug JavaScript errors easier by understanding the context

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

[LogRocket](https://logrocket.com/signup/) allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.[![LogRocket Dashboard Free Trial Banner](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/06/reproduce-javascript-errors.gif?ssl=1)](https://logrocket.com/signup/)

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier\![Try it for free](https://logrocket.com/signup/).
