Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
90
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

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

個人的にちょうど該当の問題を取り扱ったので、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 はそうしている

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
90
Help us understand the problem. What are the problem?