149
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

esModuleInterop オプションの必要性について

Last updated at Posted at 2018-02-06

個人的にちょうど該当の問題を取り扱ったので、TypeScript 2.7.1 で導入された
esModuleInterop オプションの役割と背景についてまとめておく。

なお、本来は export と import の両方に影響するオプションだが、この記事では外部の npm パッケージを import する状況のみを取り上げる。

TL;DR

Node.js 環境で import/export を使う場合、

  • esModuleInterop オプションを積極的に有効にする。
  • 関数や class を export するモジュールを import する場合、import * as _ from '_' のかわりに import _ = require('_') を使う。

モジュールの互換性

先にTypeScript の ES6 modules の解釈と allowSyntheticDefaultImports の整理という記事を読んでおくとよい。

バックグラウンドとして知っておくべきは以下の2つ:

  1. ES6 Modules は Node.js のモジュール(CommonJS)との互換性について規定がない。
  2. ES6 Modules で import/export できるものは TypeScript でいう名前空間(ざっくりいうと識別子として使えるプロパティを持つプレーンなオブジェクト) にかぎられるが、CommonJS はそうした制限がない。

Babel V.S. TypeScript

上の事情のため、Node.js のモジュールをどう扱うかはビルドツールごとに独自の仕様が設けられている。

例えば、つぎのような math モジュールを取り込もうとすると、TypeScript と Babel で異なる書き方が必要だった。

node_modules/math/index.js
var math = {
  min: function (lhs, rhs) { ...}
};
module.exports = math;

Babel:

babel.js
import math from 'math';

math.min(1, 2);

TypeScript:

tsc.ts
import * as math from 'math';

math.min(1, 2);

いいかえると、 export 側のコードはそれぞれ以下の ES6 Module として解釈されている。

Babel:

function min(lhs, rhs) { ...}

export default {
  min,
};

TypeScript:

export function min(lhs, rhs) { ...}

esModuleInterop オプションはこの振る舞いを Babel に近づけるものと考えてよい。

関数やクラスの export 問題

では TypeScript 式の何が問題だったのかというと、すでにある CommonJS モジュールが class や function を export している場合に手詰まりになること。

例えば

node_modules/min/index.js
function min(lhs, rhs) { ... }
module.exports = min;

というモジュールがあったとして、以下のコードを babel に通して実行すると、実行時エラーになる。

import * as min from 'min';

min(2, 3);

どう解決されるのか

これに対して上のオプションをつけるとどうなるのか。

まず import * と 関数呼び出しの組み合わせがコンパイルエラーになるようになる。

正しく動かしたい場合、選択肢は二つある。

一つは、以前からある syntheticDefault オプションを使用すること。ただしこのオプションはあらゆるモジュールの import/export の振る舞いを変えてしまうため、おすすめしない。

もう一つは import = を使うこと。つまり以下のようにする。

import min = require('min');

もともと互換性がない以上、これが妥当だろう。実際、DefinitelyTyped はそうしている

参考

149
89
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
149
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?