DefinitelyTyped に型定義を新規追加してみた

## まえがき

- `DefinitelyTyped` とは、言わずと知れた TypeScript 用の型定義リポジトリ。
- 昨今、特にフロントエンドでは TypeScript を使うのが当たり前となったが、まだまだ過去の遺産、型定義すらない JavaScript ライブラリも多い。
- 型定義がなければ自前で書く必要があり、せっかく書いたのなら公開して、世界のどこかで悲しむ誰かを救いたい。
- 「型定義を書く」とは、基本的に公式ドキュメントを TypeScript に変換することである。
- つまり、基本的な TypeScript の知識と根性さえあれば書くことができる。
- なので、「とりあえず OSS コントリビューターとしての実績作りしたい」みたいな下心を満たすのにも `DefinitelyTyped` は打ってつけなのである。

以下、先人達のナレッジ + 自分用に整理した **型定義を新規追加する場合の** 手順を書き残す。

## 環境

- macOS: 10.12
    - CPU: 1.8 GHz
    - Mem: 8 GB
- node: v13.12.0
- npm: 6.14.4
- 対象 package: [jexcel](https://github.com/paulhodel/jexcel) (*)

### (*) jexcel とは

- 非常に高機能な Excel ライクな表入力ライブラリ。
- 正直これが無料はちょっと引くレベル（褒め言葉）なので、ぜひ 1 度触ってみてほしい。
- 以下、jexcel + TypeScript + React のサンプル。
    - https://codesandbox.io/s/jexcel-with-react-fqh73?file=/src/App.tsx

## 準備

- GitHub 上で [DefinitelyTyped のリポジトリ](https://github.com/DefinitelyTyped/DefinitelyTyped)を Folk する。
- 以下の手順で、ローカルに環境構築する

（手順中の具体的な数字 (size など) は、2020-04-12 時点のもの。参考まで。）

```s
# git clone。約 15 分で完了。
$ git clone git clone https://github.com/${MY_NAME}/DefinitelyTyped.git
# > Receiving objects: 100% (486652/486652), 542.02 MiB | 683.00 KiB/s, done.

# いつもの
$ npm i

# 何はともあれ test を試す。6681 packages の test が実行される。約 3 分で完了。
$ npm t

# 何はともあれ lint を試す。`For a good example package, see base64-js.` とのことなので。
$ npm run lint base64-js

# 型定義のベースを作成する。`For a good example package, see base64-js.` とのことなので。(*1)
$ cp -r types/base64-js types/jexcel
$ mv types/jexcel/base64-js-tests.ts types/jexcel/jexcel-tests.ts

# 後はお好みのエディタで。
$ code .
```

### (*1) 型定義のベースの作成方法について

- 基本的には『「[dts-gen](https://github.com/Microsoft/dts-gen)」を使って型定義の雛形を生成せよ』とプルリクテンプレートに書かれている。
- しかし、dts-gen は「[Browser support in progress! This is not quite ready yet.](https://github.com/microsoft/dts-gen/tree/bff184105c86a8f7f9e2f4f9a424775cdd4f7c4c#browser)」
- つまり、ブラウザ JavaScript 実行環境特有の値 (`document` など) を使う package には非対応である。
    - jexcel はこれに該当するため、雛形は上記手順の通り cp で作成した。

## 雛形の書き換え

```diff
$ diff -r types/base64-js types/jexcel
Only in types/base64-js: base64-js-tests.ts
diff -r types/base64-js/index.d.ts types/jexcel/index.d.ts
1,3c1,3
< // Type definitions for base64-js 1.2
< // Project: https://github.com/beatgammit/base64-js
< // Definitions by: Peter Safranek <https://github.com/pe8ter>
---
> // Type definitions for jexcel 3.9
> // Project: https://github.com/paulhodel/jexcel
> // Definitions by: arx-8 <https://github.com/arx-8>
Only in types/jexcel: jexcel-tests.ts
diff -r types/base64-js/tsconfig.json types/jexcel/tsconfig.json
5c5,6
<             "es6"
---
>             "es6",
>             "dom"
21c22
<         "base64-js-tests.ts"
---
>         "jexcel-tests.ts"
```

- tsconfig.json
    - 今回の package では DOM 特有の型 (`HTMLElement`型など) が登場するため、 `compilerOptions.lib` に `"dom"` を追加した。

## 型定義を書く

- 型定義を書く。
- 型なき世界への怒りと悲しみを燃やして、根性で書く。
- 公式のドキュメントを参照したり、`console.log(typeof value)` や `console.log(value.constructor)` を駆使して、闇を切り開く。

### ツール

- 以下の 2 コマンドをエラーがなくなるまで叩く＆修正。

```s
# lint
npm run lint jexcel

# formatter
npm run prettier -- --write types/jexcel/**/*.ts
```

#### lint  の rule の無効化について

- 特にレガシーな package の場合、そう簡単に全ての型定義を書かせてくれないことが多い。
- 例えば、デフォルトでは `ban-types` で `Function` 型がエラーになる。
    - もちろん極力修正したいが、時間的制約などで全て対応できないこともある。
    - 「とりあえず関数なのはわかるが、正確なシグネチャがわからない」場合に `Function` 型を使いたい。
    - そのような場合は、`// tslint:disable-next-line ban-types` のラインコメントによる disable が推奨らしい。
        - `tslint.json` should be present and it shouldn't have any additional or disabling of rules. Just content as `{ "extends": "dtslint/dt.json" }`. If for reason the some rule need to be disabled, disable it for that line using `// tslint:disable-next-line [ruleName]`  and not for whole package so that the need for disabling can be reviewed.
- 完璧な定義より、まずは全体像の把握・本当に必要な定義の完成を目指した方が、コスパがよいと思う。きっと Done is better than perfect.

### サンプルコードを書く

- 型定義が正しいこと・予期せぬ型エラーがないことを示すため、ファイル名 `${PACKAGE_NAME}-tests.ts` にサンプルコードを書く
- このコードは、実際に処理が実行されるものではない。
- ただ型チェックのみが実行される。
- 公式のサンプルコードをコピペ・足りない型宣言を追記する等で、なるべくカバレッジを高めておく。

## 型定義の動作確認

### UnitTest

```s
npm t
```

### 自分で使ってみる

- もしその package を使用しているプロジェクトがあるなら、型定義を実際に使ってみる。
- プロジェクトの `node_modules/@types/` 配下に `index.d.ts` ファイルを置けば、 `npm i @types/*` した時と同じように動作する。
    - 例えば、今回は `${MY_PROJECT}/node_modules/@types/jexcel/index.d.ts` に定義を置いて、動作確認した。

## プルリクエスト

### 作成

- 「[Make a pull request](https://github.com/DefinitelyTyped/DefinitelyTyped#make-a-pull-request)」の手順に従う。
- Folk した自分の DefinitelyTyped のリポジトリに、プルリクエストを作成する。
- プルリクエストテンプレートのチェックリストを埋める。
- merge の from/to を確認する。
    - from: 自分の型定義追加・修正ブランチ
    - to: DefinitelyTyped/DefinitelyTyped リポジトリの master ブランチ
- 「Create pull request」

これで作業完了。お疲れさまでした。
以下、実際に作成したプルリク。
（ミス・拙い英語・時間都合で省いた定義、諸々恥ずかしいレベルだが、自戒も込めて晒しておく）
https://github.com/DefinitelyTyped/DefinitelyTyped/pull/43837

## プルリク作成後の時系列

- 基本、プルリク作成で作業完了である。
    - もちろんレビュー指摘があった場合は、状況に応じて対応が必要。
- ただし結構待ちがあり、何か手順見落としたのではとハラハラした。
- 参考までに、今回の時系列を書いておく。

2020-04-12 20:25 JST: プルリク作成
2020-04-12 21:22 JST: typescript-bot がやってきて、コメントと適切なラベルを付与
2020-04-14 08:12 JST: レビュー指摘 (1 回目) を頂く
2020-04-15 07:31 JST: レビュー指摘 (1 回目) の反映を push
2020-04-18 05:38 JST: レビュー指摘 (2 回目) を頂く（すまぬ・・・すまぬ・・・）
2020-04-18 14:22 JST: レビュー指摘 (2 回目) の反映を push
2020-04-21 06:26 JST: merged
2020-04-22 05:54 JST: typescript-bot がやってきて、npm に publish

- 計 10 日掛かった。
- 1 発で通れば 3 日、レビュー 1 回で通れば 6 日くらいか。

## その他ナレッジ

### 参考プルリクエスト探しには、GitHub label を使う

- 例えば、「型定義の新規追加のプルリクエスト」を探したい場合は、ラベル「[New Definition](https://github.com/DefinitelyTyped/DefinitelyTyped/labels/New%20Definition)」を見てみる。

## 感想

- DefinitelyTyped、巨大なリポジトリなので「git clone や vscode で開いたりしたら PC がぶっ壊れるんじゃないか」なんて謎の先入観を持っていたが、全く無問題だった。 git と vscode は偉大。
- 英語は、読む。
    - 諦めて、根性で読むしかない。Google翻訳・DeepL翻訳を駆使して、公式は読む。
    - 変に日本語訳や噛み砕いた（古い）情報を探しても「最新版は違っていた」なんてこともある。（じゃあこの記事も書くなよという話ではあるが）
    - もちろん、雰囲気を知るため簡単な日本語記事から読み始めるのはアリ。しかし、最後はやはり「公式を読め」。
- 型安全な世界を目指して、今後ともぜひ TypeScript & DefinitelyTyped やっていきましょう。