import・export を利用している TypeScript コードを HTML 上で動作させる方法

前回の続き。TypeScript コード内で importexport を用いていると、デフォルトの設定ではブラウザ上でそのコードが利用できないことを話した。

今回はこの対応方法を紹介する。

なお、今回の目的は「TypeScript の素振り環境として楽に構築できること」なので、Webpack などのバンドルツールを用いるのではなく、可能な限り素に近い状態から HTML 上で利用できるようにしてみる。

目次

AMD or UMD 形式でトランスパイルして RequireJS を使う

まずは、AMD か UMD の形式で TypeScript コードをトランスパイルして、ブラウザ上で AMD 形式のファイルを動かせるライブラリ「RequireJS」を利用する方法。

RequireJS は、CDN で読み込んでも良いし、素振り環境なら npm install しておいても良いだろう。

# 現時点では v2.3.5 がインストールされた
$ npm install -S requirejs

npm パッケージの CDN サービスは、Yarn のページから確認しやすい。

次に、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.jsimport 文を以下のように直せば構文上は正しくなる。

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 の実行環境を気にすることができたと思う。