👀

モジュールとスクリプト【個人学習まとめ】

に公開

モジュール

プログラムをモジュールという単位で分割することができます。
ソースコードの量が多くなってくると、1 つのファイルだけて管理することは難しくなります。
例えば、

  • 名前が被ってしまうこと
  • 1 つの変更が他のどの部分に影響があるか見つけることが困難であること
    などの問題があります。

この問題を解決するために、モジュールと呼ばれる仕組みがあります。

利用方法

利用するには 2 種類のキーワードが必要になります。importexportです。

まずはexportの例です。

clss.ts
const PI = 3.14;

function square(x: number) {
  return x ** 2;
}

class Rectangle {
  width: number;
  height: number;
  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

//名前付きエクスポート
export { PI, square, Rectangle };

class.ts ファイルを作成し、変数PI、関数function、クラスRectangleを用意しました。
さらに、exportキーワードを使って、外部から参照できるようにしています。
この export 方法のほかにも次のように、各変数、関数名の前にexportを記述する方法があります。

- const PI = 3.14;
+ export const PI = 3.14;

- function square(x: number) {
+ export function square(x: number) {
  return x ** 2;
}

エクスポートした物を外部ファイルで参照するには、importキーワードを使用します。

import { PI, square, Rectangle } from "./class.js";

console.log(PI);

const result = square(3);
console.log(result);

const rect = new Rectangle(5, 10);
console.log(rect.getArea());

上記の例では、calc.ts からインポートしています。
インポート時に拡張子.jsを指定しています。これは、エクスポート元ファイルの拡張子は.tsファイルですが、実際にコード上で使用されるのはコンパイルされた JavaScript ファイルであるため、.js拡張子を使います。

型のエクスポートとインポート

変数、関数と同じようにexprtキーワードを利用してエクスポートすることが可能です。

export interface Point {
  x: number;
  y: number;
}

export type LengthUnit = "m" | "cm" | "mm";

このほかにも変数、関数と同様に名前付きエクスポートとしたり型だけをまとめてエクスポートすることが可能です。

//まとめてエクスポート
export { PI, square, Rectangle, Point, LengthUnit };

//型だけをまとめてエクスポート
export type { Point, LengthUnit };

typeを指定することによって、エクスポートする要素が型であることが明確となります。
また、インポートする際には型として利用されていない場合は TypeScript がエラーを検出します。

同様にインポートもtypeを指定することによって、インポートする要素が型であることを明確化することができます。

//まとめてインポート
import { PI, square, Rectangle, Point, LengthUnit } from "./calc.js";

//型だけをまとめてインポート
import type { Point, LengthUnit } from "./calc";

では、typeを指定したインポートとエクスポートの使用例を確認してみましょう。

cals.tsでは次のようにRectangle型としてエクスポートします。

cals.ts
export { PI, square };
export type { Rectangle, Point, LengthUnit };

main.tsではRectangle型指定せずにインポートします。

import { PI, square, Rectangle } from "./calc.js";
import type { Point, LengthUnit } from "./calc";

calc.tsから型としてエクスポートされたRectanglを、main.tsでインポートしました。
このRectangleを使って、main.tsでインスタンス化(実体化)を試みます。

main.ts
const rect = new Rectangle(5, 10);'export type' を使用してエクスポートされたため、'Rectangle' は値として使用できません。
    calc.ts(15, 15): ここでは 'Rectangle ' がエクスポートされました。
console.log(rect.getArea());

エラーとなりました。Rectangleは型のみでしか利用することができません。
TypeScript ではインポートして利用するものが、モジュールからどんな状態でエクスポートされているかを把握して、エラーを検出してくれます。すごい。

モジュールとスクリプトによる名前衝突

TypeScript では、importまたはexportキーワードが含まれるファイルをモジュールとして扱います。
モジュールはグローバルスコープとは別に独自のスコープを持っており、モジュール内部で宣言された関数や変数、クラスは外部からアクセスすることはできません。
exportキーワードを使うことによって、初めて外部からアクセスすることができるようになります。

importまたはexportキーワードがないファイルはスクリプトとして扱われ、そこで宣言した巻子や変数、クラスはグローバルなものとして扱われます。

scriptA.ts
const data = "スクリプトAです。";
scriptB.ts
const data = "スクリプトBです。";

例えば上記のように 2 つのスクリプトファイルがあったとします。どちらもimportexportを持っていません。つまりグローバルスコープとして扱われます。
その結果、同じ変数名の変数dataで名前の重複となりエラーとなります。

const data = "スクリプトBです。";
→ ブロック スコープの変数 'data' を再宣言することはできません。ts(2451)
  scriptA.ts(1, 7): ここでは 'data' も宣言されました。

importexportを持っていないけど、モジュールとして扱いたい場合はexport{}を記述するとそのファイルはモジュールとして扱われるようになり、名前の重複エラーは発生しません。

const data = "スクリプトBです。";

export {};

Discussion