[TypeScript入門]モジュールについて

モジュールという言葉を聞くけど、意味を理解していなかったので、モジュールの役割と使い方をまとめます。
TypeScript学び初めの頃に、VSCodeで別ファイルなのに同じ変数名で定義すると再宣言されてるとエラーが出て、"えっ？"ってなり、解決策を調べていたところ、モジュールが関係していたので、そういったところも最後に触れます。
## 対象
- モジュールの意味や使い方が分からない方
- これからVSCodeでTypeScriptを練習しようと思っている方 → 最後のエラーの話だけでも参考になると思います
## モジュールとは
モジュールは、関数やクラスなどを効率よく再利用できるようにした仕組みです。これによって例えば、ある関数をいろんなファイルで使いたいってときは、一つのファイルのみに関数を記述し、そのファイルをモジュール化させることで、他のいろんなファイルでその関数を使いまわすことが可能になります。
## 何が嬉しいのか
- 記述量が減る
- メンテナンスしやすい
## 試してみる
### 関数を定義し、モジュール化
入力された二つの数字の四則演算を行い、結果を配列で返す関数を定義し、モジュール化します。
functionの前に"export"を記述するだけでそのファイルがモジュール化されます。
~~~function.ts
export function calculation(num1: number, num2: number) {
  const answer: number[] = [
	num1 + num2,
	num1 - num2,
	num1 * num2,
	num1 / num2,
  ]
  return answer;
}
~~~
### 別のファイルでそれを使ってみる
同じディレクトリにある別のファイルから関数を呼び出してみましょう。
import文で指定したファイルから使いたい関数を呼び出すことができるようになります。
~~~answer.ts
import { calculation } from "./function";
const answer: number = calculation(100, 10);
console.log(answer);
~~~
このファイルの実行結果は以下のとおりです。
関数の処理の結果が返ってきていますね。
~~~
$ ts-node answer.ts
[ 110, 90, 1000, 10 ]
~~~
これが基本的なモジュールの使い方です。
今回、実行コマンドに"ts-node"を使っていますが、JavaScriptファイルにコンパイルしてから、実行してもかまいません。その場合、実行ファイル（answer.ts）をコンパイルする際に、モジュール化されたファイルも同時にコンパイルされるようになっています。

改めてimport文を下記に載せておきます。これはモジュールを利用するファイルに記述する文です。カンマ区切りで複数指定することも可能です。
~~~
import { 変数名, 関数名, クラス名など・・・　} from "モジュールファイルの相対パス(拡張子(.ts)不要)"
~~~
importできるものはexportされているものだけです。モジュール化されたファイルにあるexportされていない関数などは、外部に公開されないため、import側で呼び出すことができないので、注意が必要です。
## importする際に別の名前をつけられる
importする際に関数やクラスの名前を変更することができます。
この場合、"keisan"という名前で関数calculationを利用することになります。
~~~
import { calculation as keisan } from "./function";
const answer = keisan(100, 10);
console.log(answer);
~~~
## 一括でimport
モジュールファイルに複数のexportされた変数や関数などがあるとき、一括でimportすることができます。
100円の商品を買った時に消費税8%上乗せされた時の金額を計算する処理を考えてみます。
下記のファイルでは、消費税をTAX、計算の処理を関数buyとしています。
~~~shopping.ts
export const TAX = 0.08;

export function buy(price: number, tax: number) {
  const total: number = price * (1 + tax);
  return total;
}
~~~
下記のように「* as 新しい名前」で、変数と関数をimportします。
関数を使う場合は、「新しい名前.buy」、変数を使う場合は、「新しい名前.TAX」というように、新しい名前にドット(.)をつけて利用します。
~~~price.ts
import * as shopping from "./shopping";
const total = shopping.buy(100, shopping.TAX);
console.log(total);
~~~
実行結果は下記のようになります。正しい結果が返ってきていますね。
~~~
$ ts-node price.ts
108
~~~
## デフォルトエクスポート
exportの定義がファイルに一つしかない場合は、デフォルトエクスポートを利用することができます。記述方法はexportの後に"default"を追記するだけです。最初の四則演算の関数を例として考えてみます。
~~~function.ts
export default function calculation(num1: number, num2: number) {
  const answer: number[] = [
	num1 + num2,
	num1 - num2,
	num1 * num2,
	num1 / num2,
  ]
  return answer;
}
~~~
通常のモジュールの利用との違いは、importする際にも現れます。
~~~answer.ts
import keisan from './function';
const answer = keisan(100, 10);
console.log(answer);
~~~
importの次の記述が{}無しで、しかも新しい名前がつけられています。
デフォルトエクスポートされた関数は無名としてエクスポートされるので、インポート側で必ず名前をつけてあげる必要があります。asも不要です。もちろん、エクスポート側でつけられている本来の関数名をつけてインポートしても大丈夫です。
~~~answer.ts
import calculation from './function';
const answer = calculation(100, 10);
console.log(answer);
~~~
## エラーになった原因は？
最後に、TypeScriptをVSCode上で学びはじめていて、別ファイルなのに同じ変数名で定義すると再宣言されていると怒られた話を紹介します。

一つのフォルダの中にあるファイルにconstで変数を定義し、同一フォルダ内の別のファイルで同じ変数名で変数を定義すると、「ブロックスコープの変数 '○○○' を再宣言することはできません」と表示されてしまいました（そのファイルだけ実行するならできる）。

別のファイルに記述しているのになんでだ？と当時は思っていましたが、これはVSCodeの仕様が原因だったようです。

上の説明でもあったとおり、モジュール化されたファイルでexportの記述がない関数やクラスは外部に公開されません（importできない状態）。
一方で、exportの記述がないファイルは全て公開コードとして認識され、基本的には実行対象となります（グローバルスコープとも言います）。
VSCodeでは、同一フォルダの中にある公開コードは全て一続きとして扱われます。

つまり、公開コードが複数のファイルに分かれていても、それらが一つのファイルに書かれているのと同等の扱いになってしまいます。よって、VSCodeの中では同じ変数名で二回宣言されていると判断して、怒られたみたいです。
## じゃあどうするの？
フォルダを都度作成してファイルを分けたり、別の変数名を書くというのも手ですが、できればそういった制限はかけたくないです。そこで、練習用に書いたコードを外部に公開させない手段を取ります。上の説明でも述べているとおり、モジュール化されたファイルでexportの記述がない関数やクラスは外部に公開されないので、ファイルの先頭にexport{}を記述します。
~~~
export{}
const num = 10;
function 機能名 {}
~~~
これにより、モジュール化されたファイルで変数や関数にはexportがついていないので、これらは外部に公開されないコードになります。これで、同一フォルダの中の別のファイルで同じ名前で宣言してもエラーは防げます。

他にも、グローバルスコープ化（コードの公開）を防ぎたい部分を{}で囲んでブロックスコープ化する（公開させない）こともできます。
~~~
{
　　const num = 10;
　　function 機能名 {}
}
~~~
## まとめ
- モジュールは関数やクラスなどを効率よく再利用できるようにした仕組みのこと
- exportとimportで別ファイルから関数やクラスなどを呼び出す
- 一括インポートで関数やクラスなどを一括でインポートできる
- デフォルトエクスポートで無名でエクスポートができる
- VSCodeでTypeScriptを学習するときはファイルの頭にexport{}をつけるか、公開させたくない部分を{}で囲む