ファイルモジュールの詳細
TypeScriptのモジュール機能は非常に強力であり、便利な機能です。ここでは、その強力さと実際のユースケースを説明します。
説明:commonjs、amd、esモジュール、その他
最初に、このあたりのモジュールシステムの分かりづらい矛盾を明確にする必要があります。私は単に推奨を伝え、このノイズを取り除きたいと思います。その他の動くかもしれない方法をすべて説明することはしません。
同じTypeScriptのコードからmodule
オプションに応じて異なるJavaScriptを生成されます。無視できるものは次のとおりです(既に過去のものとなった技術については詳しく説明しません):
AMD:使用しないでください。ブラウザでしか使えませんでした。
SystemJS:良い実験でしたが、ESモジュールによって置き換えられました。
ESモジュール:モダンなブラウザでのみ利用できます。
これはJavaScriptを生成するためのオプションです。これらのオプションの代わりに、module:commonjs
を使うことをおすすめします。
どのようにTypeScriptモジュールを書くかについては、まだ意見が完全には、まとまっていません。将来問題が起きることを避けるには下記のようにしてください:
import foo = require('foo')
、つまり、import/require
を避けて、ESモジュールの構文を使用してください。
それではESモジュールの構文を見てみましょう。
概要:
module:commonjs
を使い、ESモジュールの構文を使ってモジュールをimport/export/作成します。
ESモジュールの構文
変数(または型)のエクスポートは、キーワード
export
を前に置くだけです。簡単です。
変数または型を専用の
export
文でエクスポートする
名前を変更して専用の
export
文で変数や型をエクスポートする
import
を使用して変数または型をインポートする
名前を変更して_import_を使って変数や型をインポートする
import * as
を使って1つの名前にモジュールすべてをインポートする副作用のためだけに1つのファイルをインポートする:
別のモジュールからすべてのものを再エクスポートする
別のモジュールから一部のものを再エクスポートする
名前を変更して別のモジュールから一部のものだけを再エクスポートする
デフォルトエクスポート/インポート
あなたが後で知るように、私はデフォルトエクスポートが好きではありません。しかし、知っておく必要があります。そのため、ここで、デフォルトエクスポートの使い方を説明します。
export default
を使ってエクスポートする変数の前に(
let / const / var
は必要ありません)関数の前
クラスの前
import someName from "someModule"
という構文を使用してインポートする(インポートしたものには任意の名前を付けることができます):
モジュールのパス解決
私は
moduleResolution: commonjs
を指定することを前提にしています。これはあなたのTypeScript設定にも追加しておくべきオプションです。この設定はmodule:commonjs
を設定していれば、暗黙的に設定されます。
2つの異なる種類のモジュールがあります。それらは、import文のパス部分によって区別されます(たとえば、import foo from 'パス'
)。
相対パスのモジュール(
.
で始まるパス:./someFile
、../../someFolder/someFile
など)動的に参照されるモジュール(
'core-js'
、'typestyle'
、'react'
、'react / core'
など)
主な違いは、モジュールがファイルシステム上でどのように解決されるかです。
相対パスの解決
簡単です。単に相対的なパスに従います :)
ファイル
bar.ts
がimport * as foo from './foo';
を実行した場合、foo
を同じフォルダに置く必要があるファイル
bar.ts
がimport * as foo from '../foo';
を実行する場合、foo
は1つ上のフォルダ内に存在する必要があるファイル
bar.ts
がimport * as foo from '../someFolder/foo';
を実行する場合、1つ上のフォルダにfooが存在するsomeFolder
というフォルダが存在する必要がある
もしくは他に思いついた相対パスが使えます :)
動的な解決
インポートパスが相対でない場合、検索は Node.jsスタイルのモジュール解決 によって行われます。ここでは簡単な例を示します。
import * as foo from 'foo'
と書いた場合、以下の場所が順番にチェックされます./node_modules/foo
../node_modules/foo
../../node_modules/foo
ファイルシステムのルートに到達するまで続く
import * as foo from 'something/foo'
と書いた場合、以下の場所が順番にチェックされます./node_modules/something/foo
../node_modules/something/foo
../../node_modules/something/foo
ファイルシステムのルートに到達するまで続く
どのようにパスが解決されるか
上記で言及している場所が「チェックされる」とは、下記のように、その場所がチェックされることを意味しています。例えばfoo
というパスを指定しているとします:
その場所に、
foo.ts
があれば、解決されます。その場所がフォルダで、
foo/index.ts
ファイルがある場合、解決されます。その場所がフォルダで、
foo/package.json
が存在し、package.jsonのtypes
キーで指定されたファイルがある場合は、解決されます。その場所がフォルダで、
package.json
が存在し、package.jsonのmain
キーで指定されたファイルが存在する場合、解決されます。
私が言うファイルは、実際には.ts
/.d.ts
と.js
を意味しています。
以上です。あなたは既にモジュール解決のエキスパートです。
型の動的検索を上書きする
あなたは、declare module '何らかのパス'
を使ってプロジェクトのモジュールをグローバルに宣言することができます。そして、import文はそのパスを魔法のように解決します。
例:
そして、このようにすることができます:
型だけをimport/require
する
import/require
する次の文は:
実際には2つのことをしています:
fooモジュールの型情報をインポートする
fooモジュールの実行時の依存関係を指定する
開発者は、型情報のみをロードし、実行時の依存関係が発生しないようにすることも可能です。この先を理解する前に、宣言空間を読んでおくとよいでしょう。
変数宣言空間にインポートされた名前を使用しない場合、そのインポートは、生成されたJavaScriptから完全に削除されます。例を見るのが最も簡単です。これが理解できたら、ユースケースを紹介します。
例1
生成されるJavaScript:
想像の通り、fooが使われないため、空ファイルが生成されます。
例2
生成されるJavaScript:
これは、foo
(またはfoo.bas
などのプロパティ)が変数として一度も使用されないためです。
例3
生成されるJavaScript(commonjsと仮定します):
これはfoo
が変数として使われているためです。
ユースケース: 遅延ロード(Lazy loading)
型推論は事前に行う必要があります。これは、ファイルbar
でファイルfoo
のある型を使用したい場合、次のようにしなければならないことを意味します:
しかし、あるいは実行時に特定の場合だけfoo
をロードしたいかもしれません。そのような場合には、import
した名前を型アノテーションとしてのみ使用し、変数として使用しないでください。これにより、TypeScriptにより挿入される実行時依存関係をすべて削除します。そして、あなたは独自のモジュールローダーに固有のコードを書いて実際のモジュールを手動でインポートします。
例として、次のcommonjs
ベースのコードを考えてみましょう。このコードでは、特定の関数が呼び出された時だけ'foo'
モジュールをロードします:
amd
(requirejsを使用)での同様のサンプルは次のようになります:
このパターンは一般的に使用されます。
Webアプリケーションにおいて、特定のルートの場合のみ特定のJavaScriptをロードする
Nodeアプリケーションにおいて、アプリケーションの起動を高速化したい場合に、特定のモジュールだけをロードする
ユースケース:循環依存を避ける
遅延ロードの使用例と同様に、特定のモジュールローダ(commonjs/nodeと、amd/requirejs)は循環依存のため、うまく動作しません。そのような場合には、1つの方向に遅延ロードを行うようにし、反対方向のロードの前にロードしておくと便利です。
ユースケース:確実にインポートする
場合によっては、副作用のためだけにファイルをロードすることもできます(例えばCodeMirror addonsのように、モジュールを他のライブラリに登録させる場合など)。しかし、単にimport/require
を行うと、コンパイルされたJavaScriptがモジュールに依存しておらず、モジュールバンドラ(例えばwebpack)が、そのインポートを完全に無視してしまうかもしれません。このような場合、例えば下記のように ensureImport
変数を使用して、コンパイルされたJavaScriptがモジュールに依存するようにすることができます。
最終更新