typescriptで import しなくても jqueryが使えるようになっていたので、他の外部ライブラリとしてchart.jsを読み込んでみたメモ

# jQueryを使おうと思ったら、今は import しなくてもよいらしい。

chart.jsやfirebaseも同じように行けるかなと思ったけれども、そうはいかなかったのでメモ。

vueの型定義が読み込めず、typescriptのビルドが通らなかったので型定義に書いたが、
どうすればよかったのだろう。 node_modules/vue/types/vue.d.tsは確かにあったのだが、、
tscでトランスパイルしてから、webpackでバンドルする想定。

## 環境

* virtualbox 5.2.22
* vagrant 2.2.2
* Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-29-generic x86_64)
* Docker version 18.09.0, build 4d60db4
* docker-compose version 1.23.2, build 1110ad01

```docker:docker/webpack/Dockerfile
FROM node:11.5.0
WORKDIR /app
RUN npm init -y
RUN yarn add --dev @babel/core \
                  @babel/cli \
                  @babel/preset-env
RUN yarn add @babel/polyfill
RUN yarn add --dev typescript
RUN yarn add --dev tslint tslint-config-airbnb
RUN yarn add --dev webpack
RUN yarn add --dev webpack-cli
RUN yarn add --dev webpack-dev-server
RUN yarn add --dev @types/jquery
RUN yarn add --dev @types/materialize-css
RUN yarn add --dev @types/chart.js
```

```webpack.config.js
module.exports = {
  mode: MODE,
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: { loader: 'babel-loader' },
      },
    ],
  },
  // cdnから読み込むものはここに
  externals: {
    jquery: 'jQuery',
    firebase: 'firebase',
    firebaseui: 'firebaseui',
    vue: 'Vue',
    'chart.js': 'Chart',
  },
};

```


## ソース

```html:src/index.html
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-rc.2/js/materialize.min.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.4.1/firebase.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
    <script type="text/javascript" src="bundle.js"></script>
  </body>
</html>
```

上記のような感じで読み込んでいるとする。

## 読み込みイメージ

```src/index.ts
import { Chart } from 'chart.js';
import * as firebase from 'firebase';
// Vueとvueで名前が異なっており、tslintでfixされてしまうので、disable-lineを指定。
import Vue from 'vue'; // tslint:disable-line 

 $('.sidenav').sidenav();

 // document.querySelector('.my-chart')! ... (non-null-assertion-operator)を付与するとtypescriptのnullチェックによるコンパイルエラーを回避できる
const canvas = document.querySelector('.my-chart')! as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;

const data = {/*省略*/};
const options = {/*省略*/};
const myRadarChart = new Chart(ctx, {type: 'radar',options,data,});

if (!firebase.apps.length) {
  const config = require('./_config'); // tslint:disable-line no-var-requires
  firebase.initializeApp(config);
}

const app = new Vue({
  el: '#editform',
  data: {/* 省略 */ },
});

```

## jquery

上記で分かるように、突然 `$()`のように使用しても、問題はなかった。
型定義があれば、拡張も反映されており、ビルドが通る。

chart.jsなども型定義を読み込んでいるが、こちらはimportしてやらないとビルドが通らない。

## 型定義ファイル

* 以下のように定義してやれば、外部から読み込むファイルもtypescriptでビルドを通すことができる。

```src/type.d.ts
// コンパイルを通すため、外部から読み込むjsライブラリの宣言を行う。
declare module 'firebase';
declare module 'firebaseui';
declare module 'vue';

// JQueryのインタフェースの拡張を行う。
// yarn add --dev @types/materialize-cssで型定義ファイルを入手していれば不要だが、参考までに。
interface JQuery {
  sidenav(): JQuery;
}
```

[参考用：使用例](https://github.com/hibohiboo/wasureta/tree/e5581f5c018f810d6ece851af3ab9cc0cd8c9d36)


## 参考

[TypeScript の型定義ファイルと仲良くなろう][*20]
[TypeScript で型定義ファイル( d.ts )がないときの対処法][*21]
[Stack Over Flow : typescript document.getElementByIDで HTMLElement | null の型のせいでエラー][*23]
[typescriptでjqueryを拡張][*24]
[tslint ルール一覧][*25]


[*20]:http://developer.hatenastaff.com/entry/2016/06/27/140931
[*21]:https://qiita.com/Nossa/items/726cc3e67527e896ed1e
[*22]:https://stackoverflow.com/questions/43218680/document-getelementbyidid-may-be-null
[*23]:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator
[*24]:https://stackoverrun.com/ja/q/11272722
[*25]:http://neos21.hatenablog.com/entry/2017/10/25/080000

## おまけ

検証環境のpackage.json。elmなども入っているが、typescriptのバージョンなど確認用としてそのまま出力。

```json:package.json
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "watch": "chokidar '/app/**/*.pug' -p -c 'npm run build-pug' ",
    "build-pug": "pug /app/src -o /app/dist ",
    "pug": "pug -o /app/dist -P ",
    "elm-watch": "yarn run chokidar '/app/**/*.elm' -p -c 'yarn run build-elm' ",
    "build-elm": "yarn run elm make /app/src/assets/elm/ElmTest.elm --output=/app/dist/elm/ElmTest.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.2.3",
    "@types/chart.js": "^2.7.42",
    "@types/jquery": "^3.3.29",
    "@types/materialize-css": "^1.0.6",
    "autoprefixer": "^9.4.3",
    "babel-loader": "^8.0.4",
    "chokidar": "^2.0.4",
    "chokidar-cli": "^1.2.1",
    "clean-webpack-plugin": "^1.0.0",
    "copy-webpack-plugin": "^4.6.0",
    "cpx": "^1.5.0",
    "css-loader": "^2.1.0",
    "cssnano": "^4.1.8",
    "elm": "^0.19.0-bugfix2",
    "elm-format": "^0.8.1",
    "esdoc": "^1.1.0",
    "esdoc-standard-plugin": "^1.0.0",
    "eslint": "^5.11.0",
    "eslint-config-airbnb-base": "^13.1.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-jasmine": "^2.10.1",
    "file-loader": "^3.0.1",
    "globule": "^1.2.1",
    "html-loader": "^0.5.5",
    "html-minifier-webpack-plugin": "^2.0.0",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.5.0",
    "node-sass": "^4.11.0",
    "postcss": "^7.0.7",
    "postcss-cli": "^6.1.0",
    "pug": "^2.0.3",
    "pug-cli": "https://github.com/pugjs/pug-cli.git",
    "style-loader": "^0.23.1",
    "stylelint": "^9.9.0",
    "stylelint-config-sass-guidelines": "^5.3.0",
    "stylelint-order": "^2.0.0",
    "stylelint-scss": "^3.4.4",
    "tslint": "^5.12.0",
    "tslint-config-airbnb": "^5.11.1",
    "typedoc": "^0.13.0",
    "typescript": "^3.2.2",
    "uglify-js": "^3.4.9",
    "webpack": "^4.28.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.14",
    "webpack-merge": "^4.1.5"
  },
  "dependencies": {
    "@babel/polyfill": "^7.2.5"
  }
}
```