JQuery(ES6)をGulp+TypeScriptでIE対応する

# はじめに

「JSでClass使えるんや、使ってみよー」
って言ってIEで動かなくて泣いた人は私です。(今更なんですけどね)

jQuery(ES6)をTypeScriptにゆるく書き換えて静的型付けを行い、
かつES5の形式でコンパイルする、ということをしたので、その備忘録です。

# 環境

## OS
Window 10

## Node
11.13.0

## npm
6.4.1

## Gulp
4.0.2

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

必要なパッケージをプロジェクト内にインストールします、

## インストールするパッケージ一覧

- typescript
- gulp
- gulp-typescript
- gulp-sourcemaps
- del
- gulp-load-plugins
- gulp-notify
- gulp-plumber

```powershell
npm i -D typescript gulp gulp-typescript gulp-sourcemaps del gulp-load-plugins
```

# Gulpタスクの追加

gulpの設定ファイル`gulpfile.js`を`package.json`と同じ階層に作成し、設定を記述します。

```
htdocs
 ├ node_modules
 ├ package.json
 ├ gulpfile.js
 └ index.html
```

gulpfile.jsに以下の内容を記述します。
（バージョンにより書き方が異なる場合があります。）

```javascript:gulpfile.js
const gulp        = require('gulp'),
      $           = require('gulp-load-plugins')(),
      typescript  = require('gulp-typescript'),
      sourcemaps  = require('gulp-sourcemaps'),
      del = require('del'),
      setting = {
        ts: {
          path: {
            src: 'ts/**/*.ts',
            dest: 'js/'
          },
          options: {
            target: 'ES5',
            out: 'main.js',
            lib: ['ES5', 'dom']
          }
        }
      };

// TypeScript
const ts = done => {
  gulp.src(setting.ts.path.src)
    .pipe($.plumber({
      errorHandler: $.notify.onError("Error: <%= error.message %>") //<-
    }))
    .pipe(sourcemaps.init())
    .pipe(typescript(setting.ts.options))
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest(setting.ts.path.dest));

  // DONE
  done();
}

// Clean
const clean = done => {
  del([
      // assets/js/app.jsを削除
      setting.ts.path.dest+setting.ts.options.out,

      // assets/js/app.js.mapを削除
      setting.ts.path.dest+setting.ts.options.out+".map",
  ]);

  // DONE
  done();
}

// Build
gulp.task('build', gulp.series(
  gulp.parallel(
    clean,
    ts
  ),
  done => {
    done();
  }
 )
);

// Watch
gulp.task('watch', () => {
  gulp.watch([setting.ts.path.src], ts);
});

gulp.task('default', gulp.task('watch'));
```


# jQueryを使えるようにする

このままだと$をjQueryと認識してもらえなかったり、jQueryで取得したDOMがすべてany型に判別されたりしてしまうため、TypeScript内でもjQueryを使用できるよう、型定義ファイルをインストールします。

型定義ファイルの管理を行うパッケージ、typingsをインストールします。

```powershell
npm i -g typings
```

jQueryの型定義ファイルのインストールをします。

```powershell
typings install jquery --save --global
```

すると`/typing/`のディレクトリが生成されます。
この中のファイルはこのあとTypeScriptの記述部分で使用します。

# コードの書き換え

## HTML

検証のため、以下のようなHTMLを用意します。
jQueryのライブラリ自体はサイト側で読み込む必要があるため、HTMLの`<head>`内で読み込みます。

```html
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
    <title>ts test</title>

    <!-- jQueryの読み込み -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

    <!-- コンパイルされたJSファイルの読み込み -->
    <script src="./js/main.js"></script>
</head>
<body>
    <button type="button" id="btn">ボタン</button>
</body>
</html>
```


## 書き換え前のコード

jQueryで書いた元のコードです。

```javascript:/js/main.js
class Test {
    hello() {
        console.log('test')
    }
}

const onClick = elm => {
    const test = new Test();

    elm.on('click', () => {
        test.hello();
    })
}

$(() => {
    onClick($('#btn'))
})
```

## 書き換え後のコード

TypeScriptに書き換えた後のコードです。
拡張子を`.ts`に変更し、`gulpfile.js`の`src`で記述したディレクトリにコピペします。

型推論で自動で型判別してくれるため、必要のない方は記入していません。
必要に応じて型を設定してください。
今回は`onClick`の関数に引数`elm`がJQeryであることを明示しています。
`JQuery`の`J`は大文字なので気をつけてください。

先程インストールしたjQeryの型定義ファイルをtsファイルの先頭で読み込みます。

```javascript:main.ts
// jQueryの型定義ファイルの読み込み
/// <reference path="../typings/globals/jquery/index.d.ts" />

class Test {
    hello() {
        alert("Hello World")
    }
}

// TypeScriptの型定義を行っている↓
const clickFunc = (elm: JQuery) => {
    const test = new Test()

    elm.on('click', () => {
        test.hello()
    })
}

// jQueryもコンパイルを通すことができる
$(() => {
    clickFunc($('#btn'))
})
```

# gulpの実行

`gulp build`でdistファイルの削除、TypeScriptのコンパイルができるように設定しているため、それを実行します。

```powershell
gulp build
```

`/js/main.js`と`/js/main.js.map`が生成されます。
`.map`の方は、書き出されたJavaScriptの記述はTypeScriptでいうと何行目にあたる、というような情報を持っているファイルでブラウザ側で勝手に解釈してくれるため、見る必要はないです。

ファイルを監視し、保存のたびにコンパイルを行うには、以下のコマンドを実行してください。

```powershell
gulp
```

## 書き出されたJSファイル

Classなどの記述がTypeScriptのオプションで設定したES5に合わせて書き出されています。

```javascript
/// <reference path="../typings/globals/jquery/index.d.ts" />
var Test = /** @class */ (function () {
    function Test() {
    }
    Test.prototype.hello = function () {
        alert("Hello World");
    };
    return Test;
}());
var onClick = function (elm) {
    var test = new Test();
    elm.on('click', function () {
        test.hello();
        console.log("test");
    });
};
$(function () {
    onClick($('#btn'));
});

//# sourceMappingURL=main.js.map
```

これでjQueryを残したまま、レガシーなブラウザも気にせずにコーディングをすることができるようになりました。

# 余談

JSフレームワークを使うときのようモジュールで分けて読み込んだ方がコンポーネントで分けて管理できるので良さそうですね。

```javascript:test.js
module Module_Test {
    export class Test {
        hello() {
            alert('Hello World')
        }
    }
}
```

```javascript:main.js
/// <reference path="../typings/globals/jquery/index.d.ts" />
/// <reference path="./test.ts" />

import Test = Module_Test.Test;

module clickFunc {
    export function run(elm: JQuery) {
        const test = new Test()

        elm.on('click', () => {
            test.hello()
        })
    }
}

$(() => {
    clickFunc.run($('#btn'))
})
```