import・export を利用している TypeScript コードを HTML 上で動作させる方法
前回の続き。TypeScript コード内で import
や export
を用いていると、デフォルトの設定ではブラウザ上でそのコードが利用できないことを話した。
今回はこの対応方法を紹介する。
なお、今回の目的は「TypeScript の素振り環境として楽に構築できること」なので、Webpack などのバンドルツールを用いるのではなく、可能な限り素に近い状態から HTML 上で利用できるようにしてみる。
目次
- AMD or UMD 形式でトランスパイルして RequireJS を使う
- 難アリ : ESModules 形式にして直接利用する
- 失敗 : SystemJS を使って TypeScript をそのまま動作させる
- 結論 : AMD (UMD) 形式で出力して RequireJS を使おう
AMD or UMD 形式でトランスパイルして RequireJS を使う
まずは、AMD か UMD の形式で TypeScript コードをトランスパイルして、ブラウザ上で AMD 形式のファイルを動かせるライブラリ「RequireJS」を利用する方法。
RequireJS は、CDN で読み込んでも良いし、素振り環境なら npm install
しておいても良いだろう。
# 現時点では v2.3.5 がインストールされた
$ npm install -S requirejs
npm パッケージの CDN サービスは、Yarn のページから確認しやすい。
- Yarn
- bundle.run : https://bundle.run/requirejs@2.3.5
- jsDelivr : https://cdn.jsdelivr.net/npm/requirejs@2.3.5/
- unpkg : https://unpkg.com/requirejs@2.3.5/
- その他だと cdnjs より : https://cdnjs.com/libraries/require.js
次に、tsconfig.json
のモジュール指定を "commonjs"
から "amd"
もしくは "umd"
に変える。
"module": "amd",
// もしくは
"module": "umd",
UMD は AMD + CommonJS な仕様なので、どちらを指定しても変わらない。tsconfig.json
の指定を変えたら再度コンパイルすること (watch
中なら tsconfig.json
の変更自体も検知して再コンパイルしてくれる)。
HTML 側では以下のように実装することで、前回の記事で紹介した parent.js
と、そこからインポートしている child.js
が動くようになる。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TypeScript on HTML Practice 1</title>
<!-- CDN 利用時は "https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js" のように指定する -->
<script src="../node_modules/requirejs/require.js" data-main="../dist/parent.js"></script>
</head>
<body></body>
</html>
data-main
属性で指定するのはエントリポイント、つまり全てのファイルの中で最初に呼ばれる中心となるファイル。このファイルからインポート関係をたどっていってくれる。
シンプルなやり方で良い感じ。
難アリ : ESModules 形式にして直接利用する
次に、最近登場した ESModules の仕組みを利用する方法を試してみた。しかしコチラは現状難アリ。
tsconfig.json
を "module": "es2015"
に書き換え、以下のような HTML を用意する。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TypeScript on HTML Practice 2</title>
<script type="module" src="../dist/parent.js"></script>
</head>
<body></body>
</html>
これで上手くいきそうな気がするが、「dist/child
なんてファイルないよ」といエラーが出てしまう。
よくよく dist/parent.js
を見てみると、import
文がおかしい。
import { Child } from './child';
ESModules の仕様では、import
文は拡張子を含めて正確にファイルを指定しないといけない。なのでこれだと「dist/child
なんてファイルないよ」とエラーになってしまう。
dist/parent.js
の import
文を以下のように直せば構文上は正しくなる。
import { Child } from './child.js';
しかし、このように直しても、ローカルで動かすには難がある。直接 HTML ファイルをブラウザで開くだけでは、Chrome や Firefox の場合 CORS 不正を疑われてしまい上手く動かない。オプション引数を指定して起動するか、http-server
でも入れてローカルサーバを立てないといけない。
# Windows の場合
> chrome.exe --disable-web-security --user-data-dir
# Macの場合
$ open /Applications/Google\ Chrome.app/ --args --disable-web-security --user-data-dir
TypeScript の対応どうなっとるんだ?と思ったら、今のところ「トランスパイル時に import
文への拡張子付与とかしないんで」ってことみたい。tsc
コマンド単体でのトランスパイルで ESModules を実現するのはまだ厳しそう。
一応、http-server
を立て、import
文を手動で直したら、Chrome で動作させられることは確認できた。
失敗 : SystemJS を使って TypeScript をそのまま動作させる
最後に、SystemJS というライブラリを使って、ブラウザ上で TypeScript をトランスパイルして動かしてみる方法があったので、試してみた。
コチラに沿って SystemJS を用意してみたが、どうもトランスパイラを特定できないとかで上手くいかず断念。
結論 : AMD (UMD) 形式で出力して RequireJS を使おう
色々試したが、結局最初に紹介した RequireJS を使う方法が一番安全にできた。
Angular CLI から TypeScript に入ったので、Webpack もロクにいじらず、素の TypeScript の動かし方がイマイチ分かっていなかったが、これで少しは TypeScript の実行環境を気にすることができたと思う。