CDNから引っ張ったjqueryやlodashをtypescriptで使う

# はじめに
TypeScriptは近年ますます重要視されている。またCDNを活用して自社サーバーの負荷を減らし、ページの表示速度を向上する手段としても重宝されている。
ただし、ライブラリそれぞれの特性の違い、webpackの環境の違いなどで、設定でつまずくことも多々ある。

# 設定の仕方
## HTML
まずはHTMLにタグを追加し、CDNからjQueryとLodashを引っ張ってくる。CDNは別にどこでも良いが、ここは例として、[cdnjs](https://cdnjs.com/)を使う。

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.9/lodash.min.js"></script>
```

## Webpack側の設定
そしてwebpackでCDN指定したいライブラリをむき出す。
要するにwebpackのデフォルトの仕様では、ライブラリ等をbundleの中に混ぜ込み、`$`や`_`などのグロウバル変数は最終、ランダムの名前になったり、closureにラッピングされたりするので、CDNから導入する際、そういうグロウバル変数を変な形にコンパイルしないような設定が必要である。

### `externals`の設定
この設定はローカルにインストールされたLodashやjQueryを使わないように指示するための設定である。特にLodashはwebpackの依存関係にあるので、何も設定しないとローカルのLodashを使ってしまう。

```webpack.config.js
module.exports = {
  // 他の設定は省略
  externals: {
    jquery: '$',
    lodash: '_',
  }
}
```
`externals`の詳しい使い方は[こちら](https://webpack.js.org/guides/author-libraries/)を参照してください。

### `ProvidePlugin`の設定
この設定は`$`や`_`などのグローバル変数をグローバルであるように、すべてのファイルに示すための設定である。
設定しなければ、すべてのファイルに`import $ from 'jquery'`のような宣言を書かなければ行けなくなる。

```webpack.config.js
module.exports = {
  // 他の設定は省略
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.$': 'jquery',
      _: 'lodash',
      'window._': 'lodash',
    }),
  ],
}
```

`ProvidePlugin`の詳しい使い方は[こちら](https://webpack.js.org/plugins/provide-plugin/)を参照してください。

### RailsのWebpackerの場合
Webpackerの設定`config/webpack/environment.js`は若干構文は違うが、内容は一緒である。

```environment.js
// 他の設定は省略
const webpack = require('webpack')

environment.config.externals = {
  lodash: '_',
  jquery: '$',
}

environment.plugins.prepend(
  'Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    'window.$': 'jquery',
    _: 'lodash',
    'window._': 'lodash',
  })
)
```

Webpackerの[DOCS](https://github.com/rails/webpacker/blob/master/docs/webpack.md#plugins)を読めば、このあたりの書き方がわかるはず。

## TypeScript側の設定

### Type定義のインストール
本体別にいらない、型定義だけで十分。

```
npm i -D @types/jquery @types/lodash
# または
yarn add -D @types/jquery @types/lodash
```

### Global定義を実装
Project直下か、`tsconfig.json`の`"typeRoots"`に設定されたフォルダに`index.d.ts`ファイルを作成、下記コードを記入。

```index.d.ts
// Global definition
import * as _ from 'lodash'

declare global {
　　　　// 他の設定は省略
  const _: _.LoDashStatic
  // const $: JQueryStatic // jqueryはすでにGlobalに定義済み
  interface Window { // W <= 大文字, Window Classの方を拡張する
    $: JQueryStatic,
    _: _.LoDashStatic,
  }
}
```
`global`の詳しい使い方は[こちら](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#global-augmentation)を参照してください。

ここでライブラリの設計思想の違いが出てしまう：
- jQueryはできるだけGlobalで使ってほしいだろうが、`@types/jquery`で予め`$`をGlobal名前空間に置いてある。
- Lodashはfunctional programmingを推奨しており、本当は`const isObj = require('lodash/isObject')`のようなFunctionベースでの導入がベストでした。今回は無理して`_`をGlobal名前空間に出したようなものである。

### app.tsで使う
うまく設定が完了できれば、`app.ts`(またはその他一般の.ｔｓファイル)では、IntelliSenseが効いているはず。
<img width="943" alt="hospital_infos_tsx_—_byouten-junior.png" src="https://qiita-image-store.s3.amazonaws.com/0/87438/926b82c0-86df-c758-9a54-33e0ee01302c.png">
<img width="968" alt="hospital_infos_tsx_—_byouten-junior.png" src="https://qiita-image-store.s3.amazonaws.com/0/87438/da7e46ee-27e6-14b3-2eb3-9bc7e6281e4b.png">

また、webpackでコンパイルする際、`_`と`$`が外部のライブラリを使うようになっているのがわかる。

```rb
[28] external "_" 42 bytes {0} [not cacheable]
[29] external "$" 42 bytes {0} [not cacheable]
```

## 余談: `lodash/fp`の導入
`lodash/fp`は、lodashのfunctionを[カリー化](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%BC%E5%8C%96)したライブラリであり、それを使えばlodashをよりエレガントに使えることができる。ただしカリー化関数はやや理解が難しく、lodash/fpのDocumentもそこまで充実していないため、実験的に導入するのはおすすめである。

### HTML
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.9/lodash.fp.min.js"></script>
<script>
  window.fp = _.noConflict()
</script>
```

こうすれば`fp`という定数で`lodash/fp`を導入した。

### TypeScriptの設定
```index.d.ts
import * as fp from 'lodash/fp'

declare global {
　　　　// 他の設定は省略
  const fp: fp.LoDashFp
  interface Window {
    fp: fp.LoDashFp,
  }
}
```

こうすれば、`lodash/fp`も使えるようになった。

<img width="697" alt="hospital_infos_tsx_—_byouten-junior.png" src="https://qiita-image-store.s3.amazonaws.com/0/87438/3bb4317e-3949-b5e7-c8ce-32584bd90983.png">


# 参考
https://www.cnblogs.com/zichi/p/7787669.html
https://qiita.com/jkr_2255/items/283bc12dd07bc237179e
https://qiita.com/NoUkeNoLife/items/b5aa1934dc8c92459957
https://qiita.com/re-fort/items/972d9a6cdc5c00864a6e