LoginSignup
61
49

More than 5 years have passed since last update.

TypeScript の ES6 modules の解釈と allowSyntheticDefaultImports の整理

Posted at

概要

TypeScript (1.8.7) の次の項目の関連を整理する。

  • import foo from 'foo';
  • import * as foo from 'foo';
  • export default foo;
  • export = foo;
  • allowSyntheticDefaultImports option

TL;DR

  • allowSyntheticDefaultImports: false (default)
    • export = foo; / import * as foo from 'foo'; / module.exports = foo; が対応
    • export default foo; / import foo from 'foo'; / module.exports.default = foo; が対応
  • allowSyntheticDefaultImports: true
    • export = foo; / import * as foo from 'foo'; / module.exports = foo; が対応
    • export = foo; / export default foo; / import foo from 'foo'; / module.exports.default = foo; が対応

つまり、外部の npm package (module.exports = function foo() {};) を読み込む場合は、TypeScript 的には

  • option なし (default)
  • export = foo; という .d.ts
  • import * as foo from 'foo';

を使うのが良い。

外部の npm package が module.exports.default = foo; に対応している場合は、

  • option なし (default)
  • export default foo; という .d.ts
  • import foo from 'foo';

または

  • allowSyntheticDefaultImports: true
  • export = foo; という .d.ts (型定義を触れない場合など)
  • import foo from 'foo';

を使うのが良い。

ファイル

.d.ts

0.d.ts
declare module 'foo' {
  var foo: any;
  export = foo;
}
1.d.ts
declare module 'foo' {
  var foo: any;
  export default foo;
}

.ts

0.ts
import * as foo from 'foo';
foo(1 === 1);
1.ts
import foo from 'foo';
foo(1 === 1);

実行結果

option なし

$ $(npm bin)/tsc 0.d.ts 0.ts
$ $(npm bin)/tsc 0.d.ts 1.ts
1.ts(1,8): error TS1192: Module ''foo'' has no default export.
$ $(npm bin)/tsc 1.d.ts 0.ts
0.ts(2,1): error TS2349: Cannot invoke an expression whose type lacks a call signature.
$ $(npm bin)/tsc 1.d.ts 1.ts

この結果から TypeScript は次のように解釈していることが分かる。

  • export = foo; / import * as foo from 'foo'; / module.exports = foo; が対応
  • export default foo; / import foo from 'foo'; / module.exports.default = foo; が対応

--allowSyntheticDefaultImports あり

$ $(npm bin)/tsc --allowSyntheticDefaultImports 0.d.ts 0.ts
$ $(npm bin)/tsc --allowSyntheticDefaultImports 0.d.ts 1.ts
$ $(npm bin)/tsc --allowSyntheticDefaultImports 1.d.ts 0.ts
0.ts(2,1): error TS2349: Cannot invoke an expression whose type lacks a call signature.
$ $(npm bin)/tsc --allowSyntheticDefaultImports 1.d.ts 1.ts

この結果から TypeScript は次のように解釈を変えることが分かる。

  • export = foo; / import * as foo from 'foo'; / module.exports = foo; が対応
  • export = foo; / export default foo; / import foo from 'foo'; / module.exports.default = foo; が対応

export = foo;import * foo from 'foo'; だけでなく import foo from 'foo'; でも読めるようにする option なのだろう。

ちなみに import foo from 'foo'; の変換結果自体は option の有無に関係なく module.exports.default = foo; を想定して動くので、 npm package 側が対応していない場合はむやみに使うと危ないだろう。export = foo; という型定義になっており export default されていない場合にも、されているかのように解釈させる option なのだろう。

まとめ

TypeScript 的には (Babel は異なる →詳細は『Babel と TypeScript の ES6 modules の import の解釈の違い - Qiita』を参照)

外部の npm package (module.exports = function foo() {};) を読み込む場合は、

  • option なし (default)
  • export = foo; という .d.ts
  • import * as foo from 'foo';

を使うのが良い。

外部の npm package が module.exports.default = foo; に対応している場合は、

  • option なし (default)
  • export default foo; という .d.ts
  • import foo from 'foo';

または

  • allowSyntheticDefaultImports: true
  • export = foo; という .d.ts (型定義を触れない場合など)
  • import foo from 'foo';

を使うのが良い。

補足

export default foo;module.exports.default = foo; として扱うのは ES6 modules として正しい。http://www.ecma-international.org/ecma-262/6.0/#table-42 を見る限り export default foo;'default' key で export される foo (module.exports = { default: foo };)だからだ。それに対応する形だが import foo from 'foo';module.exports.default = foo;foo を使うのも正しい。http://www.ecma-international.org/ecma-262/6.0/#table-40 に表がある。

参考

61
49
0

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
61
49