bouzuya.hatenablog.com

ぼうずやのにっき

TypeScript + Babel するなら --module commonjs すべき

豪旅行の 1 日目だけど、CommonJS Interop の話を書く。

ここ数日 ES Modules と CommonJS の対応について調べていた。TypeScript と Babel および Node.js の 3 つだ。Node.js はまだ案の段階だ。現在の案は Babel に近づけている。

結論から言うと、TypeScript と Babel を併用する場合は --module commonjs にしておくと良い。ぼくの主観では TypeScript が好みなのだけど、Babel の user 数を理由に Node.js 案は Babel 寄りになりそうだ。確定ではない。TypeScript の --module es2015 とそれ以外は解釈に矛盾があるので Babel / Node.js 案との併用時に使用すべきでない。解釈の良し悪しを議論したのかは知らないが、多数決で不適切な選択がされているような印象を受けており、Babel を使うと死ぬ病気が流行ればいいのにと思っている。ちなみにぼくは併用しているので死ぬ側の人間だ。

改めて。

TypeScript と Babel の大きな違いは module.exports を ES Modules における export の ns として扱うか ns.default として扱うかだ。

解釈 1 TypeScript 。つまり module.exports を ns として扱う場合、 cjs だと ns を破壊できることになる。たとえば module.exports = 123; などができる。これは import v from 'mod'; できないし、import * as ns from 'mod'; すると ns が object にならない点でおかしい。逆に言うと ns を破壊できるだけなので module.exports = 123;import * as ns from 'mod'; すると ns === 123 になるだけのことだ。

Babel 。つまり module.exports.default を ns として扱う場合、さらに 2 つの派生系がある。module.exports.foo を ns.default.foo として扱うか、ns.foo として扱うかだ。Babel や Node.js 案は後者だ。

解釈 2 前者の場合、つまり module.exports.default を ns として扱い、module.exports.foo を ns.default.foo として扱う場合は、破壊を避けられ、一貫性がある。しかし、import {x} from 'mod'; のような名前付きの import の一切が機能しない。import v from 'mod'; const { x } = v; などと ns.default からの取り出しが必要だ。

解釈 3 後者の場合、つまり module.exports.default を ns として扱い、module.exports.foo を ns.foo として扱う場合は、破壊を避けられるが、一貫性がない。import * as ns from 'mod';module.exports を ns として扱う default 以外の場合とそうでない場合がある。

ぼくは TypeScript の解釈 1 が好きだ。2 は理想的だが対応している実装はない。module.exportsdefault とそれ以外の両方で使うという判断は、変換規則をいびつにしていると思う。

ただし、冒頭に書いたとおり Node.js の案の方向は解釈 3 だ。

個人的には数の暴力を受けている印象を受けており不愉快だけど、現実問題として Babel やその user を皆殺しにできるわけでもないので、ぼくのほうで回避するしかない。

TypeScript 側の回避策としては TypeScript の cjs interop は他と違うことを自覚して、変換される形を意識しながら利用することだ。つまり TypeScript を cjs と組み合わせる場合は --module commonjs して使う。--module es2015 したものが Babel / Node.js 案で解釈されると、default export/import で予期しない動作になるからだ。

これは Node.js の議論がどこに落ち着くかに関わらず、現時点の TypeScript + Babel 環境でも言える。TypeScript と Babel を併用する場合は --module commonjs にしておくと良い。2016-03-152016-06-20 を踏まえた、ぼくの中での結論だ。

Node.js の開発者たちに少数派だから死んでもいいと見なされている TypeScript user の参考になれば幸いだ。