[Typescript] Decorator

#Decorator
classを受け取ってデコレートする関数です。
tsconfig.jsonで"experimentalDecorators": trueにする必要があります。

＠expressionのような形式で使います。
expressionは、
デコレートされた宣言の情報を持ち、実行時に呼び出される関数として評価されます。

また、decoratorは、classの定義時に実行されています。

↓classの直前に、＠Loggingのように、適用させたいDecorator名をつけます。
↓(constructor: Function)は、他のclassでも使えるようにclass全般の型を指定しています。

```ts
function Logging(constructor: Function) {
  console.log('Logging...');
  console.log(constructor);
}

@Logging
class User {
  name = 'Quill';
  constructor() {
    console.log('User was created!');
  }
}
```

#Decorator Factories
Decoratorを返す関数を書くことを言います。
この関数を書くことで、Decoratorで引数やパラメータを使えるようになります。

```ts
function Logging(message: string) {
  return function (constructor: Function) {
    console.log(message);
    console.log(constructor);
  }  
}

@Logging('Logging User')
class User {
  name = 'Quill';
  constructor() {
    console.log('User was created!');
  }
}
```

**classとはconstructor関数である。**

#複数のDecoratorを扱う際の順番

Decoratorは下から上に実行されます。
Decorator Factoriesは上から下に実行されます。

```ts
function Logging(message: string) {
  console.log('Logging Factory');
  return function (constructor: Function) {
    console.log(message);
    console.log(constructor);
  }  
}

function Component(template: string, selector: string) {
  console.log('Component Factory');
  return function(constructor: { new(): { name: string } }) {
    const mountedElement = document.querySelector(selector);
    console.log('Component');
    const instance = new constructor();
    if (mountedElement) {
      mountedElement.innerHTML = template;
      mountedElement.querySelector('h1')!.textContent = instance.name;
    }
  }
}

@Logging('Logging User')
@Component('<h1>{{ name }}</h1>', '#app')
class User {
  name = 'Quill';
  constructor() {
    console.log('User was created!');
  }
}
```
[![Image from Gyazo](https://i.gyazo.com/b744a4aef0e62d784224a282b86d8236.png)](https://gyazo.com/b744a4aef0e62d784224a282b86d8236)

#戻り値にclassを指定して、新しいclassを作り出す

↓最後のclass Userは、完全にreturn class extends constructorのclassになります。

```ts
function Logging(message: string) {
  return function (constructor: Function) {
    console.log(message);
    console.log(constructor);
  }  
}

function Component(template: string, selector: string) {
  console.log('Component Factory');
  return function <T extends { new(...args: any[]): { name: string } } >(constructor: T) {
    
    return class extends constructor {
      constructor(...args: any[]) {
        super(...args);
        const mountedElement = document.querySelector(selector);
        const instance = new constructor();
        if (mountedElement) {
          mountedElement.innerHTML = template;
          mountedElement.querySelector('h1')!.textContent = instance.name;
        }
      }
    }
  }
}

@Logging('Logging User')
@Component('<h1>{{ name }}</h1>', '#app')
class User {
  name = 'Quill';
  constructor(public age: number) {
    console.log('User was created!');
  }
}
```

#Property Decorators
Propertyに対して適用するDecoratorsです。
引数は２つです。
class DecoratorsよりもProperty Decoratorsが先に実行されます。

↓targetは、class UserのProtoTypeになります。

```ts
console.dir(Logging);
function PropertyLogging(target: any, propertyKey: string) {
  console.log('PropertyLogging');
  console.log(target);
  console.log(propertyKey);
}

@Logging('Logging User')
@Component('<h1>{{ name }}</h1>', '#app')
class User {
  @PropertyLogging
  name = 'Quill';
  constructor(public age: number) {
    console.log('User was created!');
  }
}
```

**関数はオブジェクトである。**

↓メソッドは全部ProtoTypeの中に入る、というのが、
↓classで書いたときのデフォルトの設定になっています。
↓は、class Userが持っているProtoTypeです。
↓そのProtoTypeを、console.log(target);で取得できます。
[![Image from Gyazo](https://i.gyazo.com/b9c4db9a2989a7a24be265a2dcce452f.png)](https://gyazo.com/b9c4db9a2989a7a24be265a2dcce452f)

#Method Decorators
引数は３つです。

```ts
function MethodLogging(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log('MethodLogging');
  console.log(target);
  console.log(propertyKey);
  console.log(descriptor);
}
```

PropertyDescriptorは↓に当たります。
[![Image from Gyazo](https://i.gyazo.com/224851d03bc92055bcbd8b86ffa29a5b.png)](https://gyazo.com/224851d03bc92055bcbd8b86ffa29a5b)


↓writableは、書き換えれないものです。
[![Image from Gyazo](https://i.gyazo.com/e14400c18525ebb6b6f378a058f4e84e.png)](https://gyazo.com/e14400c18525ebb6b6f378a058f4e84e)


↓enumerableは、
↓の場合、forで回した際などに、nameをスキップさせて、表示させないというものです。
[![Image from Gyazo](https://i.gyazo.com/4ee2772eb3ec95b7ff18a3f1557951cf.png)](https://gyazo.com/4ee2772eb3ec95b7ff18a3f1557951cf)


↓configurableは、
↓のfalseにしてきた所を、もう書き換えれなくするものです。
↓この後、例えばenumerableをtrueにしようとするとエラーになります。
[![Image from Gyazo](https://i.gyazo.com/c2e3b6cd48dcc5a9eaf2b28aab78610b.png)](https://gyazo.com/c2e3b6cd48dcc5a9eaf2b28aab78610b)

#Accessor Decorators
Method Decoratorsと一緒です。

```ts
function AccessorLogging(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  console.log('AccessorLogging');
  console.log(target);
  console.log(propertyKey);
  console.log(descriptor);
}

  @AccessorLogging
  get age() {
    return this._age;
  }
  set age(value) {
    this._age = value;
  }

```

[![Image from Gyazo](https://i.gyazo.com/90a6c9ec8f8461e74cfdc6ad35a09247.png)](https://gyazo.com/90a6c9ec8f8461e74cfdc6ad35a09247)

#戻り値を使ってMethod Decoratorsを実践的にする

Class Decoratorはclassを返すことができました。
Property Decoratorsは何も返しませんでした。

MethodとAccessorは、Descriptorを返すことができます。

↓返す値によって、Property Decoratorsを変換することができます。
↓今回だったら＠enumerable(false)で変換しています。

```ts
function enumerable(isEnumerable: boolean) {
  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    return {
      enumerable: isEnumerable
    }
  }
}

  @AccessorLogging
  get age() {
    return this._age;
  }
  set age(value) {
    this._age = value;
  }
  @enumerable(false)
  @MethodLogging
  greeting() {
    console.log('Hello');
  }
```
[![Image from Gyazo](https://i.gyazo.com/4c95fc0d8a23c1b5558c0ac2caca8736.png)](https://gyazo.com/4c95fc0d8a23c1b5558c0ac2caca8736)

#Parameter Decorators
パラメータに付けることができるデコレータです。
引数は３つで、３つ目の引数にparameterIndexをつけます。
parameterIndexは、パラメータが何番目に来ているかを表現します。

```ts
function ParameterLogging(target: any, propertyKey: string, parameterIndex: number) {
  console.log('ParameterLogging');
  console.log(target);
  console.log(propertyKey);
  console.log(parameterIndex);
}

  greeting( @ParameterLogging message: string) {
    console.log(message);
  }
```

↓の0は、配列のように、Parameter Decoratorsが0番目にあるよーと言っています。
[![Image from Gyazo](https://i.gyazo.com/d7eb37d8ae9e855f38e6fbe30cba979d.png)](https://gyazo.com/d7eb37d8ae9e855f38e6fbe30cba979d)