概要
https://www.typescriptlang.org/tsconfig#moduleResolution
{
"moduleResolution": "node"
}
tsc
のモジュール解決の方法を指定します。
値はNode
かClassic
。
デフォルト値は、moduleの指定値がAMD
・System
・ES2015
のいずれかである場合はClassic
、それ以外の場合はNode
。
Classic
が元々 TS でデフォルトで使われていた方法ですが、「主に後方互換のために残してある」と明記してあるので、新しい環境でやる場合は Node
にしておきましょう。
詳細
モジュール解決(Module Resolution)とは、tsc
がimport
文を解釈する際に、そのimport
が参照する定義ファイルを特定するプロセスのことを指します。
まず、モジュール解決には relative と non-relative の 2 種類が存在します。
relative
import
文の from
で指定する文字列が/
、./
、../
のいずれかで始まる、つまりパスを指定している場合。
基本的に import
したいファイルが直接そのレポジトリに入っている場合に使用します。
non-relative
それ以外、つまり通常 npm install したパッケージを import
する時のように、いきなり単語を指定している場合。
baseUrlで指定した先、もしくはpaths
で指定したパスマッピングの先、そして module
のアンビエント宣言は、 non-relative にだけ適用されます。
その上で、TS では名前解決の方法に Node
もしくは Classic
という2種類が存在します。
Classic
(古い)
これは非常にそのままで、relative の場合はその指定したパスにあるファイルを、non-relative の場合は、指定した文字列.ts
なファイルを現在の階層も含めてレポジトリのルートディレクトリまで 1 個ずつ遡って探す、という方法です。
node_modules
とか一切関係なく、ひたすらファイル名だけが考慮されます。現代では使用することほとんど無いと思います。
Node
(現状のおすすめ)
Node
は Node.js のモジュール解決を模倣している方法です。
まず Node がどうやって名前解決するかを説明します。
Node
における、relative の場合
基本的には指定したファイルを読みにいきます。
が、Classic
と違うのは、指定したパスがディレクトリの場合に、そのディレクトリの package.json
で main
に指定したファイル、もしくはそのディレクトリ直下の index.js
を読む、という点です。
優先順位的には、 /src/path/to/a.ts
がimport ... from "./b"
していた場合、
/src/path/to/b.js
/src/path/to/main.js
(/src/path/to/package.json
が存在し、その中で"main": "main.js"
という記述があった場合のみ)/src/path/to/index.js
Node
における、non-relative の場合
Node
では、non-relative な import
はすなわちnode_modules
からの import
を意味します。
基本的な読み込みルールは上で書いた relative と変わらず(指定した単語.js
、もしくは package.json
とか index.js
とか)、node_modules
というディレクトリの中のみ探索対象とします。
Classic
と同様、階層をどんどん上に遡って見ていきます。
つまり、 /src/path/to/a.ts
がimport ... from "b"
していた場合、
/src/path/to/node_modules/b.js
/src/path/to/node_modules/b/hoge.js
(package.json
があってmain
で指定してたら)/src/path/to/node_modules/b/index.js
/src/path/node_modules/b.js
/src/path/node_modules/hoge.js
(package.json
があってmain
で指定してたら)/src/path/node_modules/b/index.js
/src/node_modules/b.js
/src/node_modules/hoge.js
(package.json
があってmain
で指定してたら)/src/node_modules/b/index.js
/node_modules/b.js
/node_modules/hoge.js
(package.json
があってmain
で指定してたら)/node_modules/b/index.js
な感じです。
さて、やっと TS における読み込み順序の話です。
TS における、relative
基本的に Node
と同じ。ただし、読み込むファイルの拡張子が.js
だけでなく
.ts
.tsx
.d.ts
となります(上記は読み込む優先度順)。
さらに、package.json
のtypes
に指定したファイルをmain
に指定したファイルの型定義ファイルとして参照します。
(package.json
のmain
に TS ファイルが指定されることはない(あったとしたらインストール後にモジュール側のコンパイルも必要になってしまう)ので、TS 的にはmain
の値は関知しません)
パッケージで提供するモジュールの型定義は@types/xxx
に置くこともできますが、公式にパッケージ自体が TS の型定義を持つのが利用者としては一番楽です。
その方法が、package.json
のtypes
に型定義ファイルを指定する、もしくはルートディレクトリにindex.d.ts
を置くことです。
TS における、non-relative
これも基本的に Node
と同じですが、relative と同様に拡張子が3種類になります。
一点明確に違うのは、node_modules/@types
配下の型定義ファイルも参照の対象になるという点です。
優先順位的には
/src/path/to/node_modules/b.ts
/src/path/to/node_modules/b.tsx
/src/path/to/node_modules/b.d.ts
/src/path/to/node_modules/b/hoge.d.ts
(package.json
のtypes
で指定されていたら)/src/path/to/node_modules/@types/b.d.ts
/src/path/to/node_modules/b/index.ts
/src/path/to/node_modules/b/index.tsx
/src/path/to/node_modules/b/index.d.ts
となります。