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

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
74
Help us understand the problem. What is going on with this article?
@bouzuya

Babel と TypeScript の ES6 modules の import の解釈の違い

More than 5 years have passed since last update.

概要

Babel と TypeScript の ES6 modules の import の挙動の違いについて確認する。 Application 内部では Babel ・ TypeScript を同一設定で使うはずなので問題は起きない。しかし、外部の npm package を利用する際には問題が起きる。

各状況ごとにどう書けばいいのかの参考情報をまとめる。

将来的には、このようなまとめが不要になることを期待する。

前提

  • Babel 6.6.5
    • es2015 preset
  • TypeScript 1.8.7
    • allowSyntheticDefaultImports (export = fooimport foo from 'foo'; できる)

各変換結果

1. Babel import ... from '...';

import foo from 'foo';

foo(1 === 1);
'use strict';

var _foo = require('foo');

var _foo2 = _interopRequireDefault(_foo);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

(0, _foo2.default)(1 === 1);

2. Babel import * as ... from '...';

import * as foo from 'foo';

foo(1 === 1);
'use strict';

var _foo = require('foo');

var foo = _interopRequireWildcard(_foo);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

foo(1 === 1);

3. TypeScript import ... from '...';

import foo from 'foo';

foo(1 === 1);
"use strict";
var foo_1 = require('foo');
foo_1["default"](1 === 1);

4. TypeScript import * as ... from '...';

import * as foo from 'foo';

foo(1 === 1);
"use strict";
var foo = require('foo');
foo(1 === 1);

実行結果

npm package の実装ごとに場合わけする。

1. ES6 module の考慮なし

function foo() { /* ... */ };
module.exports = foo;
// module.exports.default = foo;
// module.exports.__esModule = true;
  1. Babel import foo from 'foo'; は OK
  2. Babel import * as foo from 'foo'; は実行時 Error
  3. TypeScript import foo from 'foo'; は実行時 Error
  4. TypeScript import * as foo from 'foo'; は OK

いきなり矛盾している。ヤバイ。

2. ES6 module を考慮し、default に export

function foo() { /* ... */ };
module.exports = foo;
module.exports.default = foo;
// module.exports.__esModule = true;
  1. Babel import foo from 'foo'; は OK
  2. Babel import * as foo from 'foo'; は実行時 Error
  3. TypeScript import foo from 'foo'; は OK
  4. TypeScript import * as foo from 'foo'; は OK

これで 3. が OK になった。いい感じだ。

3. ES6 module を考慮し、default に export し、 __esModule を truthy に

function foo() { /* ... */ };
module.exports = foo;
module.exports.default = foo;
module.exports.__esModule = true;
  1. Babel import foo from 'foo'; は OK
  2. Babel import * as foo from 'foo'; は OK
  3. TypeScript import foo from 'foo'; は OK
  4. TypeScript import * as foo from 'foo'; は OK

__esModule が必要って時点でおかしいし、もう ES6 babel modules って呼べばいいんじゃないかな。

まとめ

ES6 modules を module.exports = function() { ... }; な外部 npm package に適用するときはよく注意する。import ... from '...';import * as ... from '...'; のどちらか一方しかきちんと動かないし、 Babel と TypeScript の解釈は矛盾しているため、それぞれ逆の記法が必要だ。

今回は問題にしなかったが TypeScript には .d.ts の問題や記法の解釈を変える option もある。がんばってほしい。→ TypeScript の ES6 modules の解釈と allowSyntheticDefaultImports の整理 - Qiita

補足

ES6 modules の仕様を見る限り、 Babel の挙動は間違っているように見える。ただ互換性の都合でそうなっていて、どうしようもないのかもしれない。

参考

74
Help us understand the problem. What is going on with this article?
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
bouzuya
ぼく、ぼうずや。なさけはひとのためならず。たのしいはせいぎ。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
74
Help us understand the problem. What is going on with this article?