オリジナルはこちら
基本的にコードサンプルなどはオリジナルを参照してください。この記事では込み入った事情の部分だけを日本語で補足します。
tsconfig.jsonの準備
tsconfig.jsonの module
設定は、TypeScript内で記述したモジュールのimport/exportをどのように解決するかを指定します。
Angular CLIのデフォルトでは module: es2015
を指定しているので、静的な import ... from
はそのまま残しますが、import()
はサポートしていません。
tsconfig.jsonで module: esnext
を指定すると、import()
をJavaScriptにそのまま残すようになります。
import()
がサポートされたブラウザ上であれば、webpackを通さなくてもそのままブラウザ上でモジュール解決できる状態になっています。
ところがまだ import()
はTC39のProposalとしてはStage 3で、未サポートのブラウザが多くあります。
現実的には、webpackを使ってbundleする必要がありますが、webpackはこの import()
をwebpackがもつ動的モジュール読み込みの仕組み ( require.ensure
) で置き換えてくれます。
つまり、 import()
のpolyfillのように振る舞ってくれます。
webpackを通すことでbundle後のJavaScriptには import ... from
も import()
も残らないため、 target: es5
のままトランスパイルしても問題ありません。つまりブラウザ互換性には影響しません。
Promiseがないブラウザではes2015のpolyfillが必要ですが、Angular CLI v7.2からはデフォルトで es2015非サポートなブラウザでだけ自動的に適用されるpolyfillを吐き出すので、我々がes2015のpolyfillについて気にすることはありません。
normalizeCommonJSImport について
これは TypeScriptの import()
の型定義でCommonJSとの互換性に問題があるための処置です。
import()
では名前付きインポートをサポートしておらず、ES Moduleにおける default export だけをサポートしています。
webpackではその互換性のために、CommonJSで書かれたモジュールを import()
でインポートするときには、module.exports
オブジェクトを default exportに見立てて、 import()
で読み込まれるオブジェクトの default
プロパティに格納しています。
TypeScriptの import()
は賢いので、 静的にimportしたときに import * as Chart from 'chart.js'
で得られる Chart
の型と、 import('chart.js').then(result => result)
で得られる result
の型は同じに扱うのですが、実際は result.default
が Chart
に相当するので、素直に書くとTypeScriptのコンパイルが通りません。
そのために normalizeCommonJSImport
でラップしています。