[webpack]ts-loaderでJavaScriptをトランスコンパイルする(babel-loader不要)

# [webpack]ts-loaderでJavaScriptをトランスコンパイルする(babel-loader不要)

[こちら](https://ttis.croud.jp/?uuid=0d5848eb-d9e8-4dd2-ba12-dae3dd861ee1)にも同じ記事を書いています

## ts-loaderはJavaScriptをトランスコンパイル出来るのか？

　ts-loaderはTypeScriptをトランスコンパイルするというのが、固定観念になっていないだろうか？しかしTypeScriptのパッケージに付属するtscではJavaScriptをコンパイル出来る。ならばts-loaderでも同様のことが出来て当然なはずだ。特にTypeScript3.7系統になってからは、JavaScriptのトランスコンパイル関する制約がかなり緩和された。ならば試してみるのが早い。

## 環境構築

### パッケージのインストール

適当なディレクトリを作り、以下のコマンドを使ってパッケージを放り込む
コンパイルはTypeScriptにやらせるので、babel類はもちろんインストールしない

```sh
yarn init -y
yarn add -D typescript ts-loader webpack webpack-cli
```

### Webpackの設定

今回はJavaScriptのトランスコンパイルが前提なので、最小限のビルド設定のみを記述している
modeをdevelopmentにしているのは、吐き出したソースの状態を視覚的に確認するためである

```webpack.config.js
const path = require("path");
module.exports = {
  mode: "development",
  //  mode: 'production',
  entry: [path.resolve(__dirname, "src/index.js")],
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist/js")
  },
  module: {
    rules: [
      {
        test: /\.(js|ts)$/,
        exclude: /node_modules/,
        loader: "ts-loader"
      }
    ]
  },
  resolve: {
    extensions: [".ts", ".js"]
  },
  devtool: "source-map"
};
```

### tsconfig.jsonの設定

ts-loaderを使う場合、JavaScriptのトランスコンパイルもtsconfig.jsonに記述することになる
重要項目はallowJsで、これをtrueに設定しておかなければJavaScriptはコンパイル出来ない

```js:src/tsconfig.json
{
  "compilerOptions": {
    "target": "es5",      /*ES5にトランスコンパイル*/
    "module": "commonjs",
    "lib": [
      "es2018",
      "dom"
    ],
    "allowJs": true, /*JavaScriptのコンパイルには必須*/
    "checkJs": true, /*JavaScriptで簡易的な型チェックを行う*/
    "sourceMap": true,
    "inlineSources": true,
    "outDir": "../../dist",
    "strict": true,
  }
}
```


## テスト用プログラムの作成

アロー関数とfor-ofはES5へのトランスコンパイルが行われる
bodyの取得にquerySelectorを使っているのは後でTypoの実験をするためなので気にしないで欲しい

```src/index.js
addEventListener("DOMContentLoaded", () => {
  const message = "動作テスト\nを行う\nサンプル";
  const body = document.querySelector("body");
  if (body) {
    for (const msg of message.split("\n")) {
      const node = document.createElement("div");
      node.innerHTML = msg;
      body.appendChild(node);
    }
  }
});
```

```dist/index.html
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
  <meta charset="utf-8">
  <title>ts-loader sample</title>
  <script type="text/javascript" src="js/bundle.js"></script>
</head>
<body>
</body>
</html>
```

## ビルドとその結果

### ビルド

```sh
yarn webpack
```

### 出力コード(コメント部分は除去)

見事にES5へのトランスコンパイルが行われている
ts-loaderでJavaScriptのトランスコンパイルに成功したのだ！

```dist/js/bandle.js
"use strict";

addEventListener("DOMContentLoaded", function () {
    var message = "動作テスト\nを行う\nサンプル";
    var body = document.querySelector("body");
    if (body) {
        for (var _i = 0, _a = message.split("\n"); _i < _a.length; _i++) {
            var msg = _a[_i];
            var node = document.createElement("div");
            node.innerHTML = msg;
            body.appendChild(node);
        }
    }
});
```


## ts-loaderの利点

- babel-loaderがいらない
ts-loaderだけでトランスコンパイル出来るので、babel-loaderは必要なくなる
polyfillのような機能は無いので、@babel/coreは必要に応じて追加すれば良い

- 簡易的な型チェックが利用できる
たとえば

```js
const body = document.querySelector("body");
```

　を

```js
const body = document.querySelect("body");
```

　というTypoをしたとすると、トランスコンパイル時に以下のように出力される

```
TS2551: Property 'querySelect' does not exist on type 'Document'. Did you mean 'querySelector'?
```

　JavaScriptを使っていながら、TypeScriptの恩恵にあずかれるのだ

- TypeScriptとJavaScriptのやりとりにd.tsが不要となる
TypeScript3.7系統はJavaScriptのコンパイル時、d.tsを自動生成する能力を持っている。この機能が働くと、TypeScriptからJavaScriptのファイルをimportするときに、d.tsを手動で作らなくてもそのまま読み込むことが可能だ。

- JavaScriptからTypeScriptへの移行が楽になる
JavaScriptをメインで使っている人も、ある日TypeScriptでコードが書きたくなったときに必要なのは拡張子をtsにしたファイルを作ることだけだ


## jsとtsのプログラムを混在させる

```src/add.ts
export function Add(a: number, b: number) {
  return a + b;
}
```

```src/index.js
import { Add } from "./add";

addEventListener("DOMContentLoaded", () => {
  const body = document.body;
  if (body) body.innerHTML = `10+20=${Add(10, 20)}`;
});
```

結果

```
10+20=30
```

JavaScriptからTypeScriptを呼び出しているが、もちろん逆も可能だ。

## まとめ

**JavaScriptとは型指定を省略できるTypeScriptなのだ**

という感覚でプログラムが書ける。
これからTypeScriptへ移行しようかと迷っている方々は、とりあえずローダーだけts-loaderに差し替えてみてはどうだろうか？