$ ryokkkke.com/TypeScript/tsconfig.json

rootDirs

概要

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

{
  "rootDirs": ["src/views", "generated/templates/views"]
}

複数のディレクトリを仮想的にマージして、一つの基準のディレクトリとみなしてコンパイルできるようにします。

仮想的なマージなので、このオプションは実際に出力する JS にはなんら影響を及ぼしません。

詳細

基本的にこのオプションが有効なのは、型定義ファイルをコードから自動で生成している場合です。

仮想的な root directory

rootDirsで複数のディレクトリを指定すると、仮想的にそれらのディレクトリを一つのディレクトリにマージしてimportを解釈してくれます。

src/
├── dir1
│   ├── hoge
│   └── piyo
│       └── piyopiyo
└── dir2
    ├── hoge
    │   └── hogehoge
    └── piyo

↑ 例えばこういったディレクトリ構造があったとして、dir1dir2を以下のようにrootDirsに指定すると、

{
  "rootDirs": ["src/dir1", "src/dir2"]
}

TypeScript コンパイラは root directory を以下のようなディレクトリだとみなしてコンパイルを実行します。

src/
└── dir1
    ├── hoge
    │   └── hogehoge
    └── piyo
        └── piyopiyo

つまり、同じ名前のディレクトリが同じ階層に存在している場合、実際は別のディレクトリであっても同じディレクトリだとみなす、ということです。

これは、型定義ファイルをコードから自動生成している場合に役立ちます。

例えば

例えば、typed-css-modulesという、CSS Modules を使う際に CSS の型定義ファイルを自動で作成してくれるツールがあります。
こちらを使用していると基本的に全 CSS ファイルと同じ階層に必ず.css.d.tsファイルが生成されるわけですが、何気にファイル数が増えてしまうんですよね。

こういう時に、自動生成される型定義ファイルを手で修正を加えるディレクトリとは別のディレクトリに入れるようにしておき、その両方をrootDirsで指定することで、機能を果たしつつ更新頻度の高いディレクトリをすっきり保つことができます。

と思っていたら、開発者ご本人が解説されているのを発見したのでご紹介しておきます。
TypeScript 2.0 の Module Resolution Enhancements について - Qiita

自動生成されるコードは手で修正を加えるとめんどうなことになる(もしくは無駄足になる)ことがほとんどだと思うので、

  • 手で修正を加えるディレクトリsrc/
  • 自動生成されるディレクトリgenerated/
  • コンパイル結果が入るディレクトリdist/

のような区分ができるのはかなり気持ちいい感じがしますね。
後からジョインしたメンバーにも「どれが自動生成でどれが手動修正なのか」がわかりやすいなどのメリットもあると思います。
(自動生成のコードを知らずに手で修正して「ビルドするとなぜか修正が消える…」みたいなことをやった経験ある)

ちなみに、前提となる型定義ファイルについて

TypeScript では、TS(.ts)ファイル以外をimportする時、指定されたディレクトリにある型定義(.d.ts)ファイルの型を読み込みます。

src/
├── hoge.ts
├── index.ts
└── piyo.js
// hoge.ts
export const hoge = "HOGE";

// piyo.js
export const piyo = 123;

// index.ts
import { hoge } from "./hoge";
import { piyo } from "./piyo";

console.log(hoge.trim());
console.log(piyo.trim()); // piyoはnumberなので、trimプロパティを持たないが...

↑ 例えばこういったファイル群があったとして、これをこのままコンパイルすると、エラーなくコンパイルできてしまいます。
piyo.jsに型定義ファイルが存在しないので、piyoany型になるからですね。

ただし、もちろん上記の通りpiyonumbertrimプロパティを持たないので、ランタイムエラーになります。

ちなみに、strict: truenoImplicitAny: true)になっている場合は下記のようにコンパイルエラーになります。

src/index.ts:2:22 - error TS7016: Could not find a declaration file for module './piyo'. 'src/piyo.js' implicitly has an 'any' type.

2 import { piyo } from "./piyo";
                       ~~~~~~~~
Found 1 error.

こういう時に、型定義(.d.ts)ファイルを同じディレクトリに作ることで、piyo.jsを型付きでimportできるようになります。

// src/piyo.d.ts
declare const piyo = 123;

export { piyo };

この状態でコンパイルすることで、出て欲しかった「123trim は無いよ」というエラーでコンパイルが止まります。

src/index.ts:5:18 - error TS2339: Property 'trim' does not exist on type '123'.

5 console.log(piyo.trim());
                   ~~~~
Found 1 error.