$ ryokkkke.com/TypeScript/tsconfig.json

moduleResolution

概要

https://www.typescriptlang.org/tsconfig#moduleResolution

{
  "moduleResolution": "node"
}

tsc のモジュール解決の方法を指定します。
値はNodeClassic
デフォルト値は、moduleの指定値がAMDSystemES2015のいずれかである場合はClassic、それ以外の場合はNode

Classic が元々 TS でデフォルトで使われていた方法ですが、「主に後方互換のために残してある」と明記してあるので、新しい環境でやる場合は Node にしておきましょう。

詳細

モジュール解決(Module Resolution)とは、tscimport文を解釈する際に、その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.jsonmain に指定したファイル、もしくはそのディレクトリ直下の index.js を読む、という点です。
優先順位的には、 /src/path/to/a.tsimport ... from "./b"していた場合、

  1. /src/path/to/b.js
  2. /src/path/to/main.js/src/path/to/package.jsonが存在し、その中で"main": "main.js"という記述があった場合のみ)
  3. /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.tsimport ... from "b"していた場合、

  1. /src/path/to/node_modules/b.js
  2. /src/path/to/node_modules/b/hoge.jspackage.json があってmainで指定してたら)
  3. /src/path/to/node_modules/b/index.js
  4. /src/path/node_modules/b.js
  5. /src/path/node_modules/hoge.jspackage.json があってmainで指定してたら)
  6. /src/path/node_modules/b/index.js
  7. /src/node_modules/b.js
  8. /src/node_modules/hoge.jspackage.json があってmainで指定してたら)
  9. /src/node_modules/b/index.js
  10. /node_modules/b.js
  11. /node_modules/hoge.jspackage.json があってmainで指定してたら)
  12. /node_modules/b/index.js

な感じです。

さて、やっと TS における読み込み順序の話です。

TS における、relative

基本的に Node と同じ。ただし、読み込むファイルの拡張子が.jsだけでなく

  1. .ts
  2. .tsx
  3. .d.ts

となります(上記は読み込む優先度順)。

さらに、package.jsontypesに指定したファイルをmainに指定したファイルの型定義ファイルとして参照します。
package.jsonmainに TS ファイルが指定されることはない(あったとしたらインストール後にモジュール側のコンパイルも必要になってしまう)ので、TS 的にはmainの値は関知しません)

パッケージで提供するモジュールの型定義は@types/xxxに置くこともできますが、公式にパッケージ自体が TS の型定義を持つのが利用者としては一番楽です。
その方法が、package.jsontypesに型定義ファイルを指定する、もしくはルートディレクトリにindex.d.tsを置くことです。

TS における、non-relative

これも基本的に Node と同じですが、relative と同様に拡張子が3種類になります。
一点明確に違うのは、node_modules/@types配下の型定義ファイルも参照の対象になるという点です。

優先順位的には

  1. /src/path/to/node_modules/b.ts
  2. /src/path/to/node_modules/b.tsx
  3. /src/path/to/node_modules/b.d.ts
  4. /src/path/to/node_modules/b/hoge.d.tspackage.jsontypes で指定されていたら)
  5. /src/path/to/node_modules/@types/b.d.ts
  6. /src/path/to/node_modules/b/index.ts
  7. /src/path/to/node_modules/b/index.tsx
  8. /src/path/to/node_modules/b/index.d.ts

となります。