TypeScript 3.0 の Project reference(プロジェクト参照) やってみた

TypeScript 3.0 がリリースされました。

* [Announcing TypeScript 3.0 / TypeScript](https://blogs.msdn.microsoft.com/typescript/2018/07/30/announcing-typescript-3-0/)

追加機能のひとつ、Project references は、ちょうど仕事で「どうするのがいいの？」と迷ってたところだったので、さっそくやってみました。

話としてはよくある、 **複数のプロジェクトから参照される "共通プロジェクト" の在り方** です。

## Project Reference 適用以前

Project Reference 適用前（つまり現状）は、次のような構成になっていました（説明簡略化のため、client -> shared のみを書いてますが serverside からも shared を参照しています）。

```
root
├── client
│   ├── tsconfig.json
│   └── src
│       └── main.ts
└── shared
    └── src
        └── calc.ts
``` 

**shared の calc.ts**

```typescript
export function calcAdd(x: number, y: number): number {
    return x + y;
}
```

**client の main.ts**

```typescript
import { calcAdd } from '../../shared/src/calc';

console.log(calcAdd(1, 2)); // = 3
```

**client の tsconfig.json**

```json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  }
}
```

``shared/`` 配下 は **ただのファイル置き場** で、client から相対パスで ``calc.ts`` を参照しているに過ぎません。

これを ``tsc -b client/tsconfig.json`` した結果は次のようになります。

```
root
├── dist
│   ├── client
│   │   └── src
│   │       └── main.js
│   └── shared
│       └── src
│           └── calc.js
├── client
│    └─-
└── shared
     └─-
``` 

これはイケてないと思いつつ開発してきましたが、これを Project reference に変えてみます。

## Project reference 適用後

ではプロジェクト参照を使ってみます。TypeScript Version 3.0.1 で試しています。

まず、 shared/ をプロジェクト化するために ``tsc --init`` で ``tsconfig.json`` を作り、内容を次のようにします。

**shared の tsconfig.json**

```js
{
  "compilerOptions": {
    // tsc --init で既定で設定されてた項目
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,

    // あとから追加した項目
    "outDir": "../dist/shared",
    "rootDir": "./src",
    "composite": true,
    "declaration": true,
  }
}
```

``"composite": true`` がプロジェクト参照のために必要な項目で、``"declaration": true`` は、型定義ファイルを出力するために必要です(よね？)。``outDir`` と ``rootDir`` は出力される .js ファイルの場所を調整するために設定しました。

次に client 側の ``tsconfig.json`` を修正します。

**client の tsconfig.json**

```js
{
  "compilerOptions": {
    // tsc --init で既定で設定されてた項目
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,

    // あとから追加した項目
    "outDir": "../dist/client",
    "rootDir": "./src",

    // さらに追加した相対パス地獄を防ぐための項目
    "baseUrl": "./",
    "paths": {
      "shared/*": [
        "../dist/shared/*"
      ]
    }
  },
  // あとから追加した項目
  "references": [
    { "path": "../shared" }
  ]
}
```

こちらには、 ``"references"`` を追加し、shared への参照を設定します。これがプロジェクト参照のメインですね。

``baseUrl`` と ``paths`` は、 "relative path hell" を回避するための設定です。

* [モジュール解決 / TypeScript 日本語ハンドブック / js STUDIO](http://js.studio-kingdom.com/typescript/handbook/module_resolution)

を見てやってみました。

最後に、client の ``main.ts`` の ``import`` 文を書き換えます。

**client の main.ts**

```typescript
import { calcAdd } from 'shared/calc';

console.log(calcAdd(1, 2)); // = 3
```

import は、shared プロジェクトのビルド結果である ``./dist/shared`` を参照するようにしますが、 先に ``baseUrl`` と ``paths`` で ``shared/*`` に ``../dist/shared/*`` をマッピングさせているので、ここでは ``from 'shared/calc'`` だけで済みます。

ではビルドしてみましょう。

```
tsc -b client/tsconfig.json
```

を実行します。ポイントは、shared もプロジェクトなのにそれは含めていない、ということです。

ビルド結果を含むディレクトリ全体は次のようになります。

```
root
├── client
│   ├── tsconfig.json
│   └── src
│       └── main.ts
├── shared
│   ├── tsconfig.json
│   └── src
│       └── calc.ts
└── dist
    ├── client
    │   └── main.js
    └── shared
        ├── calc.js
        └── calc.d.ts
```

なんだかそれっぽくなった気がします。
``tsc -b client/tsconfig.json`` としたのに、プロジェクト参照に設定されている shared 側も（先に）ビルドされて ``dist/shared`` に出力されています。

client の ``tsconfig.json`` には ``"rootDir": "./src"`` を設定したので、好き勝手に別の親ディレクトリにある .ts ファイルを参照することができなくなり、秩序が守られる気がします。

冒頭の説明には、もっとたくさんのオプションについて説明がありますが、とりあえず以上です。