[TypeScript] Try decorators
Tags: typescript
Published: 2021-10-03 16:13:57

## Intro
This time, I will try decorators.
At first I wanted to implement init only setter property of C# in TypeScript.

As a result, I couldn't do that in this time.
Because the decorators are executed only on runtime.

So I just tried using them in this time.

### Environments
* TypeScript ver.4.4.3
* Webpack ver.5.56.0
* webpack-cli ver.4.8.0

## About decorators
I think the decorators are special kinds of functions for classes and class members.
I can't use them for functions, enums, and etc.

```ts
// Compile error: Decorators are not valid here
@message("Hello World")
export function funcSample() {
}
```

I can add decorators into classes, methods, properties, accessors, and paramters.
They are separated by their arguments.

#### main.page.ts
```ts
import { Sample } from "./sample";

function init(): void {
    const s = new Sample();
    s.userName = 'Masanori';
    s.greet('Hello!');
}
init();
```
#### sample.ts
```ts
import { classDecorator, methodDecorator, methodDecoratorFactory } from "./decoratorSample";

// Class decorators
@classDecorator
export class Sample
{
    private _userName = '';
    // Accessor decorators(get/set)
    // as same as the method decorators.
    @methodDecorator
    public get userName() {
        return this._userName;
    }
    public set userName(value: string) {
        this._userName = value;
    }
    public constructor() {
        console.log("Hello constructor");
    }
    // Method decorators
    @methodDecorator
    // use factory functions to add any arguments
    @methodDecoratorFactory("Hello world!")
    public greet(message: string): void {
        console.log('greet');
        console.log(`${this._userName}: ${message}`);
    }
}
```
#### decoratorSample.ts
```ts
export function classDecorator(constructor: Function): void {
    console.log("classDecorator");
    console.log(constructor);
}
export function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log('methodDecorator');
  console.log(target);
  console.log(propertyKey);
  console.log(descriptor);
}
export function methodDecoratorFactory(args: string) {
  console.log('methodDecoratorFactory');
  console.log(args);
  
  return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => 
      methodDecorator(target, propertyKey, descriptor);
}
```

#### Result
```
methodDecorator
{constructor: ƒ, greet: ƒ}
userName
{enumerable: false, configurable: true, get: ƒ, set: ƒ}
methodDecoratorFactory
Hello world!
methodDecorator
{constructor: ƒ, greet: ƒ}
greet
{writable: true, enumerable: false, configurable: true, value: ƒ}
methodDecorator
{constructor: ƒ, greet: ƒ}
greet
{writable: true, enumerable: false, configurable: true, value: ƒ}
classDecorator
class Sample {
    _userName = '';
    get userName() {
        return this._userName;
    }
    set userName(value) {
        this._userName = value;
    }
    constructor() {
        console.log…
Hello constructor
greet
Masanori: Hello!
```

* [TypeScript Documentation - Decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)

## Accessing or modifying classes and class members
For example, I can call caller methods from the decorators.

#### decoratorSample.ts
```ts
export function classDecorator(constructor: Function): void {
  console.log("classDecorator");
  // call caller methods
  console.log(constructor.prototype['greet']());
}
export function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('methodDecorator');
    // "target" is instance of the "Sample". 
    console.log(target);
}
```

And I can modify targets like below.

#### decoratorSample.ts
```ts
...
export function ModClassDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
  console.log("ModClassDecorator");
  return class extends constructor {
    addedText = "added";
  }
}
...
```

I must use generics to do this.
If I don't use generics and set the argument type as any, I will get an error.

#### decoratorSample.ts
```ts
...
export function ModClassDecorator2(constructor: any) {
  console.log("ModClassDecorator2");
  return class extends constructor {
    addedText = "added";
  }
}
...
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2x0ebnyymgja0q4yti83.png)