TypeScript　まとめ３（モジュール化）

## TypeScriptまとめ集
1. [型について](https://qiita.com/hiro-hiro/items/f2dc671f48e2599e923b)
1. [ユーティリティ型](https://qiita.com/hiro-hiro/items/0f0201a8a49db06e0032)
1. [モジュール化](https://qiita.com/hiro-hiro/items/9879a297cf5b30c474e2)
1. [インストール〜コンパイル方法](https://qiita.com/hiro-hiro/items/52108807c0594798e1c3)

## 目次
1. [クラス化](#1クラス化)
1. [モジュール化](#2モジュール化)
1. [デコレーター](#3デコレーター)

## 1.クラス化
ECMAScript2015以降より他言語同様**クラス化**が可能に。

オブジェクト思考言語によくある以下の例を設定できる。

* アクセス修飾子
* getter, setter
* コンストラクター
* 静的メンバー
* 継承
* オーバーライド
* 抽象メソッド
* インターフェイス
など。

### アクセス修飾子
アクセス修飾子は以下の３種類

* public...自由にアクセス可能、デフォルトのアクセス修飾子
* protected...同じクラス、または派生クラスのメンバーからアクセス可能
* private...同じクラスからのみアクセス可能

```typescript:typescript(アクセス修飾子)
class Sample{

  public hello():string{
    return `こんにちは、Qiitaさん!`;
  }
}

let s = new Sample();
s.hello();  //こんにちは、Qiitaさん!
```

### コンストラクター
インスタンス化をしたと同時に実行される。

```typescript:typescript(コンストラクター)
class Sample{

  private name:string;

  constructor(name:string){
    this.name = name;
  }
  //コンストラクターは以下のように書くこともできる
  //コンストラクターの引数にアクセス修飾子を付与する
  constructor(private name:string){}

  public hello():string{
    return `こんにちは、${this.name}さん!`;
  }
}

let s = new Sample('Taro');
s.hello();  //こんにちは、Taroさん!
```

### getter, setter
* getブロックでgetterを実装できる
* setブロックでsetterを実装できる

```typescript:typescript(getter,setter)
class Sample{

  private _name!:string;

  //getter
  get name():string{
    return this._name;
  }

  //setter
  set name(value:string){
    this._name = value;
  }
}

let s = new Sample();
s.name = 'Taro';
console.log(s.name); //結果:Taro
```

### 静的メンバー
インスタンスを生成しなくてもメソッドを実行できる。

```typescript:typescript(静的メンバー)
class Sample{
  //静的プロパティ
  public static name:string = 'Taro';

  //静的メソッド
  public static hello(name:string):string{
    return `こんにちは、${this.name}さん!`;
  }
}

let name:string = Sample.name;
Sample.hello(name)；
```

### 継承
元になるクラスの機能の引き継ぎ、新しい機能の追加、元の機能の一部を修正などができる。

```typescript:typescript(継承)
//継承元
class Sample1{
  protected name:string;

  constructor(name:string){
    this.name = name;
  }

  hello():string{
    return `こんにちは、${this.name}さん!`;
  }
}

//継承先
class Sample2 extends Sample1{
  //派生クラス独自のメソッドを定義
  bye():string{
    return `さようなら、${this.name}さん。`;
  }
}

let s = new Sample2('Taro');
s.hello();  //こんにちは、Taroさん!
s.bye();    //さようなら、Taroさん。
```

### オーバーライド
継承時、基底クラスで定義済のメソッド/コンストラクターを派生クラスで上書きできる。

```typescript:typescript(オーバーライド)
class Sample2 extends Sample1{
  protected age:number;
  
  //コンストラクターをオーバーライド
  constructor(name:string, age:number){
    super(name);
    this.age = age;
  }

  //helloメソッドをオーバーライド
  hello():string {
    return super.hello() + `年齢は${age}ですね?
`; 
  }
}
```

### 抽象クラス、抽象メソッド
派生クラスで機能を上書きすること前提にする。

```typescript:typescript(抽象クラス、抽象メソッド)
//抽象クラスを宣言
abstract class Sample1 {
  abstract hello():string;
}

//継承先
class Sample2 extends Sample1{
  hello():string{
    return `こんにちは、${this.name}さん!`;
  }
}
```

### インターフェイス
すべてのメソッドが抽象メソッドであり、複数のインターフェイスを同時に継承することが可能。

```typescript:typescript(インターフェイス)
interface Sample1{
  name:string;  
  hello():string;
}
  
interface Sample2{
  bye():string
}
  
class Sample3 implements Sample1,Sample2{
  
  name:string;
    
  constructor(name:string){
      this.name = name;
  }   
  hello():string{
    return `こんにちは、${this.name}さん!`;
  }
  bye():string{
    return `さようなら、${this.name}さん。`;
  }
}
```

インターフェイスを継承して、新しいインターフェイスを宣言することもできる

```typescript:typescript(interfaceの継承)
interface Interface1 extends Interface2{ ...}
```

## 2.モジュール化

### モジュールとは？

* 機能ごとにファイルを分割してまとまりを持った処理を作る。
* 1モジュールに対して1ファイルが基本。
* 代表的なモジュールシステムは**CommonJS**と**ES Module**の２種類の方式がある。
* CommonJSはサーバー用、ES Moduleはブラウザ用で分けると良い。
* ES ModuleはECMA Script6で追加されたため、古いブラウザだと使えない可能性があるため注意。

### モジュールの定義
* CommonJSでは、**module.exports**で定義する。
* ES Moduleでは、**export**をつけて定義できる。

**CommonJSの場合**

```typescript:typescript（CommonJSでのモジュール定義)
//関数のモジュール化
module.exports= function showMessage():void {
  console.log(`ようこそ、Qiita!`);
}

//クラスのモジュール化
class Util {
  static getVersion():string{
    return '1.0.0';
  }
}

module.exports = Util;
```

**ES Moduleの場合**

```typescript:typescript（ESModuleでのモジュール定義)
//これはexportがついていないため、アクセスできない
const TITLE:string = 'Qiita';

//関数のモジュール化
export function showMessage():void{
  console.log(`ようこそ、${TITLE}!`);
}

//クラスのモジュール化
export class Util {
  static getVersion():string{
    return '1.0.0';
  }
}

//------------------------------------------
//別の書き方も可能
class Util { ... }

//そのままエクスポート
export { Util };

//リネームしてエクスポート
export { Util as AppInfo };
```

### モジュールのインポート

* 別ファイルで定義したモジュールを使用することができる。（インポート）
* CommonJSでは、**require**をつけて定義する。
* ES Moduleでは、**import**をつけて定義できる。

**CommonJSの場合**

```typescript:typescript（CommonJSでのインポート)
//インポートするモジュールを選択する
let a = require('Util');
let b = require('showMessage');

//インポートした関数を使用する
a.showMessage();
console.log(b.Util.getVersion());
```

**ES Moduleの場合**

```typescript:typescript(ESModuleでのインポート)
//インポートするモジュールを選択する
//from ~ は相対パスから拡張子を取り除いた形式で表す
import { showMessage, Util } from './App';

//インポートした関数を使用する
showMessage();
console.log(Util.getVersion());
```

モジュール配下のメンバーに別名を付与したい場合

```typescript:typescript(モジュールのインポート(別名))
//別名を定義
import { showMessage as myMessage, Util as MyUtil } from './App';

//インポートした関数を使用する
myMessage();
console.log(MyUtil.getVersion());
```

モジュール配下のメンバーを全てインポートしたい時

```typescript:typescript(モジュールのインポート(全て))
//「*」で全てインポートできる
//as区で別名を定義すること
import * as aaa from './App';

//インポートした関数を使用する
aaa.showMessage();
console.log(aaa.Util.getVersion());
```

既定のエクスポートをインポート
モジュール配下の一つのメンバーに対してdefaultキーワードを付与することで、インポート時に関数/クラスの名前を指定する必要をなくすことができる。

```typescript:typescript(既定のエクスポートをインポート)
export default class {
  static getVersion():string{
    return '1.0.0';
  }
}

//import側
import app from './App';

console.log(app.getVersion());
```

任意のタイミングでモジュールをインポートする(遅延インポート、動的インポート)
今までの静的インポートと言う。

```typescript:typescript(遅延インポート)
//Promiseオブジェクトを生成
import('./App')
  .then(app => {
    app.showMessage();
  });

//async関数
async function main(){
  let app = await import('./App');
  app.showMessage();
}
```

### 再エクスポート

```typescript:typescript(再エクスポート)
//Hoge/Fooモジュール指定されたメンバーを再エクスポート
export { HogeUtil } from './Hoge';
export { FooUtil } from './Foo';

//Barモジュールすべてのメンバーを再エクスポート
export * from './Bar';
```

### 名前空間
モジュールのさらに上の階層を定義して、インポート・エクスポートするモジュールをまとめる。
古い言い方だと、内部モジュールという。（外部モジュールは普通のやつ）

```typescript:typescript(名前空間)
//namespaceを宣言する
namespace MainApp{
  export class Hoge { ... }
  export function too() { ... }
}

//名前空間配下のクラス・関数を呼び出し
let mah = new MainApp.Hoge();
MainApp.foo();
```


名前空間自体に階層儲けることも可能

```typescript:typescript(階層付き名前空間)
namespace Wings.MainApp{
  export class Hoge{}
  export function foo() {}
}
let wmah = new Wings.MainApp.Hoge();
Wings.MainApp.foo();

//以下のような書き方も可能
namespace Wings{
  export namespace MainApp{
    export class Hoge{}
    export function foo(){}
  }
}
```

## 3.デコレーター

### デコレーターとは
クラスやプロパティ、メソッド、引数などに対して付与できる一種の修飾子。

例：Angularの場合

```typescript:デコレーターの例
//以下の書き方で、<my-app>要素がAppComponentクラスで処理される、という意味になる
@Component({
  selector:'my-app',
  templateUrl:'./app.html',
  styleUrls:['./app.css]
})

export class AppComponent {...}
```

### デコレーターの一般的な記法

```
@name(args, ...)
・name: デコレーター名
・args: 任意の引数
```

* <strong>複数のデコレーターを列挙することも可能</strong>
この場合myMethod2を@deco1デコレーター、@deco2デコレーターで就職していることとなる
@deco1
@deco2
myMethod(){ ... }

### デコレーターを定義してみる
自分でデコレーターを定義してみることも可能。
下記は一例。

```typescript:デコレーターの定義
//logデコレーターを宣言
function log(target: any, key: string, desc: PropertyDescriptor){

  //オリジナルのメソッドを退避
  let origin = desc.value;

  //メソッドの書き換え
  desc.value = function(){

    //元メソッドの実行前に行う処理
    console.log(`${key} start...`);
    let start = Date.now();

    //元メソッドを実行
    let result = origin.apply(this, arguments);

    //元メソッドの実行後に行う処理
    let end = Date.now();
    console.log(`${key} end...`);
    console.log(`Process Time ${end - start}ms`);

    //元のメソッドの戻り値を返す
    return result;
  }
}

```

### デコレーターの利用
上記で定義した@logデコレーターを利用する場合...

```typescript:
class MyClass{
  @log
  test():string{
    return 'TEST';
  }
}

let c = new MyClass();
c.test();
```

**ちなみに、デコレーターは引数付きでも定義が可能**

```typescript:引数付きデコレーターの利用
//定義
function log(tag: string) { ... }

//利用
@log('TEST')
test():string { ... }
```