TypeScriptコードのコンパイルや TSLint 時にエラーが出る状態なら Commit できないように

## 使うパッケージ

- [TypeScript](https://github.com/Microsoft/TypeScript) » コンパイルするため
- [TSLint](https://github.com/palantir/tslint) » lint するため
- [husky](https://github.com/typicode/husky) » `git commit`時に`npm run precommit`とか実行させるため。`npm run precommit`が正常に終わった時だけ実際に`commit`とかする。
- [lint-staged](https://github.com/okonet/lint-staged) » 対象ファイルを適当なコマンドへ渡すため

## 準備

まずは上記のパッケージをインストール。

```bash
npm init -y # package.json 作成
npm i -D typescript tslint husky lint-staged
```

そして、`tsconfig.json`と`tslint.json`を作っておきます。

```bash
npx tsc --init
npx tslint --init
```

また、こんな感じのエラーが起きる`.ts`ファイルも作っときます。

```ts:index.ts
const foo: number = 'foo';
```

> ```bash
tree -I node_modules # node_modules は非表示
# .
# ├── index.ts
# ├── package-lock.json
# ├── package.json
# ├── tsconfig.json
# └── tslint.json

## `lint-staged`の設定

`package.json`の`lint-staged`プロパティへ設定を書きます。
インデックスされてるファイルの中に`key`（ファイルのパターン）にマッチするものがあれば、それを`value`（run-scripts)に書かれたすべてのタスクに渡して実行します。

> `key`のパターンは`minimatch`というのを使ってるらしく、迷ったら
 https://npm.runkit.com/minimatch 辺りでマッチするか試しながらやるといいかも。

設定をこのようにすると、インデックスされている`index.ts`を`npm run typescript -- index.ts && npm run tslint -- index.ts`で実行する感じにできます。

```json
{
  "lint-staged": {
    "*.{ts,tsx}": [
      "typescript",
      "tslint"
    ]
  }
}
```

## `run-script`に`typescript`と`tslint`を登録

`run-scripts`は`scripts`プロパティに書いていきます。

以下の`tsc --project .`や`tslint --project .`は、`tsconfig.json`の設定を使って色々処理できるやつで、まぁCLIではそんな感じで使ってると思います。

> `--noEmit`を使うと`.js`ファイルがアウトプットされなくなります。

ということで、こんな感じに書けば良さそうに思えます。が、これはエラーで終わります。

```json
{
  "scripts": {
    "precommit": "lint-staged",
    "typescript": "tsc --project . --noEmit",
    "tslint": "tslint --project .",
  }
```

多分`tsc --project . foo.ts`のようなコマンドが実行されてるのですが、`--project`フラグと`foo.ts`のようなファイル指定は一緒にはできない感じのようです。

> ```bash:error.log
Option 'project' cannot be mixed with source files on a command line.
```

なので、`typescript`へは`tsconfig.json`の`compilerOptions`で必要な設定すべてをフラグにして渡す必要があります。

> [`json-flag`](https://github.com/nju33/json-flag)という`npm`パケを作りました。こんな感じで`compilerOptions`をフラグに変換したものをコンソールに出せます。
>
> ```bash
> npx json-flag -p compilerOptions tsconfig.json
> # --target es5 --module commonjs --strict true
> ```

なので、先ほどをこんな感じに書き換えます。

```json
{
  "scripts": {
    "precommit": "lint-staged",
    "typescript": "tsc --target es5 --module commonjs --strict true --noEmit",
    "tslint": "tslint --project .",
  }
}
```

### 追記(2017-12-05)

上の`typescript`についてです。全部対象になりますが、`"typescript": "tsc -p . --noEmit #"`というように最後に`#`を置いて渡ってきたファイル名をコメント扱いにするほうが簡単でした。

## 準備できたので成果をCommitする

上記の`index.ts`を使ってれば、`git commit`して時こんな感じになってコケます。

> たまに何故か`precommit`ホック動かない時があるので、再度`npm i -D husky`してください😱

```bash
git init
git add index.ts
git commit -m 'Commit'

# husky > npm run -s precommit (node v8.5.0)
# 
#  ❯ Running tasks for *.{ts,tsx}
#    ✖ typescript
#      → index.ts(1,7): error TS2322: Type '"foo"' is not assignable to type 'number'.
#      tslint
# 
# ✖ typescript found some errors. Please fix them and try committing again.
# index.ts(1,7): error TS2322: Type '"foo"' is not assignable to type 'number'.

# husky > pre-commit hook failed (add --no-verify to bypass)
```

（上記の工程でやってるとして）`typescript`を修正しても`tslint`でもコケます。

> ```diff:index.ts
- const foo: number = 'foo';
+ const foo: string = 'foo';
```

```bash
# husky > npm run -s precommit (node v8.5.0)
#
#  ❯ Running tasks for *.{ts,tsx}
#    ✔ typescript
#    ✖ tslint
#      → ERROR: /Users/nju33/test/hogeaaa/index.ts[1, 21]: ' should be "
# ✖ tslint found some errors. Please fix them and try committing again.
# 
# ERROR: /Users/nju33/test/hogeaaa/index.ts[1, 21]: ' should be "
# 
# husky > pre-commit hook failed (add --no-verify to bypass)
```

> ```diff:index.ts
- const foo: string = 'foo';
+ const foo: string = "foo";
```

`tslint`を修正してようやく成果を`commit`できました〜。

```bash
# husky > npm run -s precommit (node v8.5.0)
# 
#  ✔ Running tasks for *.{ts,tsx}
# [master (root-commit) 789b35b] Commit
#  1 file changed, 1 insertion(+)
#  create mode 100644 index.ts
```

`ci`で`lint`がコケまくってるプロジェクトにいいと思います😇