ts-node で TypeScript + node をサクッと実行する

node でさっくりと TypeScript を実行したいときに、babel なり webpack なりで transpile して実行するのは不便です。しかも型が考慮されなくなってしまいますね。

そういうときは [ts-node](https://github.com/TypeStrong/ts-node) を使うことで、バベってファイルを生成してから食わせずとも、そのまま実行できるので手軽です。型も考慮されます。

## 使い方

さっくりと依存を追加します:

```
npm install --save typescript ts-node
```

`tsconfig.json` もついでに生成しておきます:

```
> ./node_modules/.bin/tsc --init

message TS6071: Successfully created a tsconfig.json file.
```

試しに適当なコードを書いてみます:

```typescript:src/main.ts
const main = () => {
    console.log('It works!');
};

main();
```

動かしてみます:

```
> ./node_modules/.bin/ts-node src/main.ts

It works!
```

いい感じにすぐ実行されました。

もちろん型が誤っている場合もきちんとエラーになりますし、元のファイルの情報が表示されます:

```typescript:src/main.ts
const main = (str: string) => {
    console.log('It works!');
};

main(1111);
```

```
TSError: ⨯ Unable to compile TypeScript:
src/main.ts:5:6 - error TS2345: Argument of type '1111' is not assignable to parameter of type 'string'.

5 main(1111);
       ~~~~
```

:+1:

## エイリアス

`import some from '../../a/b.ts'` とかになってややこしいので、パスのエイリアスを登録しておくことが結構あると思います:

```json:tsconfig.json
{
  "compilerOptions": {
    // ...
    "baseUrl": "./",
    "paths": {
      "#/*": ["src/*"]
    },
    // ...
  }
}
```

この例では 

```typescript:src/x.ts
import some from '#/a/b.ts';
// <=> import some from './a/b.ts';
```


```typescript:src/i/j/k.ts
import some from '#/a/b.ts';
// <=> import some from '../../a/b.ts';
```

となります。捗りますね。

これで `import` するスクリプトの階層にかかわらず、ソースルートディレクトリからのパスとして記述できるので、すっきりとしてわかりやすくなります。VSCode でもエイリアスが効いていい感じです。

Vue でも `@/components/a.ts` で参照できるようなエイリアスになってますが、似たような感じですね。 (あれは webpack のエイリアスのようですが)。

---

例として次のようなファイル構成にしてみましょう:

```typescript:src/mod-a/build.ts
export default (): string => 'mod-a';
```

```typescript:src/mod-b/repeat.ts
import build from '#/mod-a/build';

export default (number: number): string => {
    let queue = [];
    const value = build();
    for (let i = 0; i < number; ++i) {
        queue.push(value);
    }

    return queue.join(' | ');
}
```

```typescript:src/main.ts
import repeat from "#/mod-b/repeat";

const main = () => {
    console.log(repeat(5));
};

main();
```

さて、これでエイリアスを使って読み込めたので
実行してみたいと思います:

```
> ./node_modules/.bin/ts-node src/main.ts

Error: Cannot find module '#/mod-b/repeat'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at Object.<anonymous> (/Users/user/Sandbox/my-ts-project/src/main.ts:1:1)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Module.m._compile (/Users/user/Sandbox/my-ts-project/node_modules/ts-node/src/index.ts:473:23)
    at Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Object.require.extensions.(anonymous function) [as .ts] (/Users/user/Sandbox/my-ts-project/node_modules/ts-node/src/index.ts:476:12)
    at Module.load (internal/modules/cjs/loader.js:599:32)
```

しかしながら、これは `import` が解決できずエラーになります。なぜでしょうか。

この問題は 
[tsconfig-paths](https://github.com/dividab/tsconfig-paths) を追加して、`require` してあげることで解決できます:

```
npm install --save tsconfig-paths
```

```
> ./node_modules/.bin/ts-node --files -r tsconfig-paths/register src/main.ts

mod-a | mod-a | mod-a | mod-a | mod-a
```

`tsconfig-paths` を `require` してあげることで、`tsconfig.json` で定義したエイリアスで `import` を解決できるようになりました。

ただ、毎回入れるのは面倒なので `package.json` の `scripts` に追加して `npm start` 一発で実行できるようにすると楽でいいです:

```json:package.json
{
  // ...
  "scripts": {
    "start": "ts-node --files -r tsconfig-paths/register",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}
```


```
npm start src/main.ts
```

:+1:

## 型定義

自分で型定義 (`*.d.ts`) を持っている場合は、これも `tsconfig.json` に追加します:

```json:tsconfig.json
{
  "compilerOptions": {
    // ...
    "typeRoots": ["./src/types"],
    // ...
}
```


## テスト

テストに関しては別エントリで記述しました。同じくバベらずとも `ts-jest` を使うことで型を活かしながら簡単にテストができます:

👉 [TypeScript のテストを Jest (ts-jest) でやってみる](https://qiita.com/mangano-ito/items/99dedf88d972e7e631b7)

---

これで TypeScript で手元の適当なコードを動かしたいときから、バッチ等でゴリゴリ動かしたいときまで簡単に対応できます。