TypeScriptでpower-assertを使う時の注意点

TypeScriptでpower-assertを使ってテスト時に詳細情報を出そうとしたら, テスト実行そのものは問題ないのだが詳細情報が出なくて解決するまで小一時間かかってしまいました.  せっかくなので注意点として共有しておきます.

## **<font color="red">2016/02/17 追記</font>**
[DefinitelyTypedのpower-assert.d.ts側で対応された](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/8127)ため、本記事で書いていた注意点について気にする必要がなくなりました！ :tada:

最新のDefinitelyTypedのpower-assert.d.tsを使って、

```ts
import * as assert from 'power-assert';
```
または

```ts
import assert = require('power-assert')
```

でpower-assertを`import`して使用すればOKです. 型定義を使いつつ、power-assertによる詳細情報も出るようになります。


## 前提

前提として、commonjs モジュールとしてtsファイルをコンパイルする場合のケース. つまり`tsconfig.json`が以下のような場合のプロジェクト. 既存資産を利用する際の一般的な構成かと思います.

```json:tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5" // または "es6"
  }
}
```

また、動作確認した各ライブラリのバージョンは下記の通り. 2015/12/21時点の最新安定版です.

```json:package.json
  "devDependencies": {
    "mocha": "^2.3.4",
    "power-assert": "^1.2.0",
    "typescript": "^1.7.5"
  }
```

tsd.configは下記の通りで, DefinitelyTypedの型定義を使用.

```json:tsd.config
{
  "version": "v4",
  "repo": "borisyankov/DefinitelyTyped",
  "ref": "master",
  "path": "typings",
  "bundle": "typings/tsd.d.ts",
  "installed": {
    "mocha/mocha.d.ts": {
      "commit": "40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
    },
    "power-assert/power-assert.d.ts": {
      "commit": "40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
    },
    "power-assert-formatter/power-assert-formatter.d.ts": {
      "commit": "40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
    },
    "node/node.d.ts": {
      "commit": "40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
    }
  }
}
```

## 問題の原因
power-assertを使う場合はassertの変数名が重要. 下記のようにpower-assertをrequireする際に`assert`にしないと詳細情報が出力されない. 最終的なJavaScriptがちゃんとこの形になるようにすれば問題は解決しました.

```js
var assert = require('power-assert');
```

## 駄目な書き方
ES6でpower-assertを使う例としてよく出てくる下記の書き方をそのままTypeScriptに持ってくると駄目(ここが落とし穴).

```ts:ts
import assert from 'power-assert';
```

コンパイルすると`power_assert_1`という別名に置き換えられてしまう...!

```js:tsc後のjs
var power_assert_1 = require('power-assert');
```

テストがパスする場合は当然気づきません. テストが失敗してエラー詳細を見ようとしたときに, あれ？出ていない？ と気づきます.


## OKな書き方1

tsファイルで直接requireを書くと大丈夫(当たり前ですが...). ただし, DefinitelyTypedのpower-assertの型定義が使えなくなるのでこの方法はイマイチ.

```ts:ts
const assert = require('power-assert');
```

```js:tsc後の.js
var assert = require('power-assert');
```


## OKな書き方2

tsファイルでは以下のように書く.

```ts:ts
import * as assert from 'power-assert';
```

すると望みの形式になる.

```js:tsc後の.js
var assert = require('power-assert');
```

ただしこの書き方にすると, 最新のDefinitelyTypedの型定義(`commit 40c60850ad6c8175a62d5ab48c4e016ea5b3dffe`)だとpower-assertの利用箇所でエラーになるという, 別の問題が起きるようになる.

例えば以下のようなテスコートを書いている場合.

```ts
import * as assert from 'power-assert';

import add from '../src/sample'

describe("Sample", () => {
    context("add()", () => {
        it("should return sum", () => {
            const sum = add(1, 1);
            assert.equal(sum, 2);
        });
    });
    
    context("add() failed", () => {
        it("should return sum", () => {
            const sum = add(1, 2);
            assert.equal(sum, 2);
        });
    });
});
```

tscでJavaScriptに変換すると以下のエラーとなる.

```bash
$ tsc
test/sample-test.ts(9,20): error TS2339: Property 'equal' does not exist on type 'typeof "power-assert"'.
test/sample-test.ts(16,20): error TS2339: Property 'equal' does not exist on type 'typeof "power-assert"'.
```

このエラーは読み込んでいる`typings/power-assert/power-assert.d.ts`の最下部を下記の様に修正すれば一応解消します.

``` ts:修正前(40c60850ad6c8175a62d5ab48c4e016ea5b3dffe)
declare module "power-assert" {
    export default assert;
}
```

``` ts:修正後
declare module "power-assert" {
    export = assert;
}
```

ただし, https://github.com/DefinitelyTyped/DefinitelyTyped/commit/8ab759a80cfd768be64d2215e935a73347bc68ea#diff-72ca08ef2bf693879d4d12e977032b57 のコミットでdefaultでexportされるように意図的に修正されているようなので, 何か別の回避方法があるのかもしれません. もしくは, tscでrequireに変換せずにES6のimportのまま出力させる場合はうまくいくのかも(未確認).

`typings/power-assert/power-assert.d.ts`をいじりたくない! という方は, 上記のコミット前のバージョンを使うことでも回避できます. 下記の様に`98d3168c2c570352a3a7393788e2ec9fbb08ade6`を指定してインストールすればOK.

```json:tsd.json
{
  "version": "v4",
  "repo": "borisyankov/DefinitelyTyped",
  "ref": "master",
  "path": "typings",
  "bundle": "typings/tsd.d.ts",
  "installed": {
    "mocha/mocha.d.ts": {
      "commit": "40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
    },
    "power-assert/power-assert.d.ts": {
      "commit": "98d3168c2c570352a3a7393788e2ec9fbb08ade6"
    },
    "power-assert-formatter/power-assert-formatter.d.ts": {
      "commit": "40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
    },
    "node/node.d.ts": {
      "commit": "40c60850ad6c8175a62d5ab48c4e016ea5b3dffe"
    }
  }
}
```


## まとめ

* commonjs モジュールとしてtsファイルをコンパイルする場合は, `import assert from 'power-assert';`ではなく`import * as assert from 'power-assert';`を使用する.
* ただし現在のDefinitelyTypedの`power-assert.d.ts`と相性が良くないので, 手動で直すか古いバージョンの`power-assert.d.ts`を使う必要あり.
* 変にハマりたくなければ, `const assert = require('power-assert');`をとりあえず使っておいて回避するのも手.