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

# 概要

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

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

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

## .ts

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

```ts: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](http://qiita.com/bouzuya/items/408a09c17d29b1267003)』を参照)

外部の 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 に表がある。

# 参考

- http://www.ecma-international.org/ecma-262/6.0/#table-40
- http://www.ecma-international.org/ecma-262/6.0/#table-42
- [Babel と TypeScript の ES6 modules の import の解釈の違い - Qiita](http://qiita.com/bouzuya/items/408a09c17d29b1267003)
- http://blog.bouzuya.net/2016/03/14/