「このコード、何をしてるんだっけ?」と、自分が書いたはずのコードを前に頭を抱えた経験はないでしょうか。3か月前の自分は、もはや他人も同然です。そんなとき、適切に書かれたJSDocコメントがあれば、コードの意図を瞬時に把握できます。
実は、JSDocはJavaScript向けのドキュメント記法として生まれましたが、TypeScriptと組み合わせることで驚くほど強力なツールへと進化します。型情報とドキュメントが一体になることで、開発者体験が格段に向上するのです。この記事では、JSDocの基本から始めて、TypeScriptプロジェクトでの実践的な活用方法、チーム開発でのルール作りまで幅広く取り上げていきます。
JSDocコメントの基本を押さえよう
JSDocについて「聞いたことはあるけど、ちゃんと書いたことがない」という方は意外と多いものです。プロジェクトの中で断片的に使われているのは見かけるけれど、体系的に学ぶ機会がなかった、という声もよく耳にします。ここでは、JSDocの基礎をしっかり固めておきましょう。
JSDocコメントは、関数やクラス、変数の直前に書く特別な形式のコメントです。通常のコメントが // や /* */ で書かれるのに対し、JSDocは /** */ というスラッシュとアスタリスク2つの形式で記述します。この小さな違いが、IDEやドキュメント生成ツールにとっては大きな意味を持ちます。単なるメモ書きではなく、構造化された情報として扱われるわけです。
そういえば、JSDocの歴史は意外と古く、Javadocの影響を受けて2000年代初頭に登場しました。JavaScriptに型がなかった時代から、開発者たちはJSDocで型情報を補完してきたのです。TypeScriptが登場した現在も、JSDocは役割を変えながら重要な存在であり続けています。型を書くだけでは伝わらない「なぜその設計にしたのか」「どんな前提条件があるのか」を記述できるのは、JSDocならではの強みです。
覚えておきたい基本タグ
JSDocには多数のタグがありますが、日常的に使うものはそれほど多くありません。実務で頻繁に登場するのは、@param、@returns、@throws、@example、@deprecatedあたりです。これらをきちんと書けるだけで、コードの可読性は飛躍的に向上します。
@paramは関数のパラメータを説明するタグで、引数の名前、型、そして説明文を記述します。TypeScriptでは型情報はすでに定義されているため、JSDocでは型を省略して説明文に集中するという使い方が効果的です。「この引数にはどんな値を渡すべきか」「nullを渡した場合はどうなるか」といった補足情報を書くことで、利用者が安心してAPIを呼び出せるようになります。
@returnsは関数の戻り値を説明するタグです。TypeScriptの戻り値型と合わせて使うことで、型だけでは伝わらない情報を補足できます。たとえば、配列を返す関数があったとして、型からは「文字列の配列」であることはわかっても、「ソート済みかどうか」「重複を含むかどうか」は型だけでは表現できません。こういった情報こそ、JSDocで記述すべきものです。
@exampleと@deprecatedの活用
@exampleタグは、実際のコード例を記載できる非常に便利なタグです。使い方がわかりにくい関数やクラスに対して、具体的なコード例を添えておくだけで利用者の理解が格段に早まります。ドキュメントを読むよりもコード例を見たほうが早い、という開発者は多いものです。IDEによってはホバー時にサンプルコードが表示されるため、わざわざドキュメントサイトを開く必要もなくなります。
@deprecatedタグは、ある機能が非推奨になったことを示すために使います。このタグをつけると、多くのIDEではその関数やプロパティの呼び出し箇所に取り消し線が引かれます。視覚的なフィードバックがあるため、チームメンバーが古いAPIを誤って使ってしまうリスクを大幅に減らせます。代替となる関数やメソッドがある場合は、その情報も一緒に記載しておくと親切です。
ところで、@throwsタグも見落とされがちですが重要な存在です。TypeScriptの型システムは例外に関する情報を持っていないため、「この関数はどんな条件で例外を投げるのか」はJSDocで明示するしかありません。特にチーム開発では、エラーハンドリングの方針を共有するためにも、@throwsをしっかり書く習慣をつけておきたいところです。
TypeScriptとJSDocの連携がもたらす力
TypeScriptとJSDocは、それぞれ単体でも強力ですが、組み合わせることで真価を発揮します。型情報が言語レベルで保証されつつ、意図や背景がドキュメントとして補完される、というのが理想的な形です。この組み合わせが開発現場にどんなメリットをもたらすのか、具体的に見ていきましょう。
TypeScriptプロジェクトでJSDocを書く最大の利点は、型情報とドキュメントの二重管理が不要になることです。JavaScript時代のJSDocでは @param {string} name のように型情報を手書きしていましたが、TypeScriptでは型が言語仕様に組み込まれているため、JSDocでは説明文だけを書けばよくなります。これにより、型とドキュメントが乖離するリスクを排除できます。実際のところ、型情報をJSDocにも書いてしまうと、型を変更したときにJSDocの更新を忘れて不整合が生じる、というトラブルが起こりがちです。
実は、TypeScriptのコンパイラ自身がJSDocの情報を読み取って活用します。tsconfig.jsonでdeclarationとdeclarationMapを有効にすると、JSDocの内容が.d.tsファイルに反映されます。つまり、ライブラリを公開する際にも、利用者がJSDocの恩恵を受けられるのです。npm パッケージとして配布するようなプロジェクトでは、この仕組みが特に重要になります。
そういえば、TypeScript 5.0以降では@satisfiesや@overloadといったJSDocタグのサポートも強化されています。純粋なJavaScriptプロジェクトでも、JSDocを通じてTypeScriptの型チェックの恩恵を受けられるようになりました。// @ts-checkというコメントをファイルの先頭に書くだけで、JSDocの型アノテーションに基づいたチェックが走ります。段階的にTypeScriptを導入したいプロジェクトにとって、これは非常に心強い選択肢です。
ジェネリクスやユニオン型との組み合わせ
TypeScriptの高度な型機能とJSDocを組み合わせる場面も少なくありません。ジェネリクスを使った関数では、型パラメータが何を表しているのかをJSDocで説明することで、利用者の理解を助けられます。たとえば @template T というタグを使って、型パラメータの意味を記述できます。TやUといった一文字の名前は慣例的に使われますが、それだけでは意図が伝わりにくい場合も多いためです。
ユニオン型やインターセクション型を使った複雑な型定義にも、JSDocは力を発揮します。型の定義自体は読めても、「なぜこの組み合わせにしたのか」という設計意図は型だけでは伝わりません。特にAPIの境界部分では、「このパラメータがstring型の場合はIDとして扱われ、number型の場合はインデックスとして扱われる」といった使い分けの説明が必要です。こうした情報をJSDocで記載しておくと、後から参加したメンバーも迷わずにコードを読み進められます。
ところで、TypeScriptの@linkタグをJSDoc内で使えることをご存知でしょうか。関連する型やメソッドへのリンクを記述できるため、ドキュメント内のナビゲーションが容易になります。大規模なプロジェクトでは、関連するコードが複数のファイルに散らばっていることが多いため、@linkを活用して参照先を明示しておくと、コードリーディングの効率が大きく向上します。
VSCodeでのJSDoc活用テクニック
開発者の多くがVSCodeを使っている現在、JSDocとVSCodeの連携は押さえておきたいポイントです。実は、VSCodeのTypeScript言語サービスはJSDocの情報を非常に深く解析しており、適切にJSDocを書くだけで開発体験が劇的に向上します。
VSCodeでは、関数やメソッドにカーソルを合わせるとホバー情報が表示されますが、この情報にはJSDocの内容が含まれます。パラメータの説明、戻り値の説明、使用例まで、すべてホバーパネルに表示されるのです。わざわざ定義元のファイルを開かなくても、必要な情報を手元で確認できます。チームメンバーが書いた関数を使うとき、この恩恵は特に大きく感じられるでしょう。
そういえば、VSCodeの自動補完(IntelliSense)もJSDocの情報を活用しています。オブジェクトのプロパティを入力しようとすると、各プロパティの説明が補完候補と一緒に表示されます。大量のプロパティを持つ設定オブジェクトを扱う場面では、どのプロパティが何を意味するのか一目でわかるため、ドキュメントとエディタを行き来する手間がなくなります。
実は、JSDocのスニペット生成もVSCodeの便利な機能のひとつです。関数の直前で /** と入力してEnterを押すと、パラメータや戻り値に基づいたJSDocテンプレートが自動生成されます。あとは説明文を埋めるだけなので、JSDocを書く心理的なハードルを下げる効果があります。TypeScriptの型情報からテンプレートが生成されるため、型名を手打ちする必要もありません。
拡張機能でさらに便利に
VSCodeのマーケットプレイスには、JSDocの記述を支援する拡張機能がいくつか公開されています。「Document This」は、関数やクラスを選択するだけで詳細なJSDocテンプレートを生成してくれる拡張機能です。標準のスニペットよりも高機能で、ジェネリクスやオーバーロードにも対応しています。大量の関数にJSDocを追加する場面では、この拡張機能が時間を大幅に節約してくれるでしょう。
「Better Comments」は、JSDocの表示をカスタマイズできる拡張機能です。@deprecatedのコメントを赤色で表示したり、@todoを目立つ色にしたりと、視覚的にコメントの重要度を区別できるようになります。チーム全体でこの拡張機能を導入すると、コメントの見落としを防ぐ効果も期待できます。
ところで、ESLintのJSDoc関連プラグインも忘れてはなりません。eslint-plugin-jsdocを導入すると、JSDocの書き忘れや書式の不備を自動でチェックしてくれます。パブリックな関数にJSDocが書かれていない場合に警告を出す設定にすれば、ドキュメントの網羅性を機械的に担保できます。CI/CDパイプラインに組み込むことで、JSDocなしのコードがマージされることを防げるのです。
チーム開発でのJSDocコメント規約
ひとりで開発しているうちはJSDocの書き方に困ることは少ないかもしれません。しかし、チームで開発するようになると「どこまで書くか」「どんな形式で書くか」が人によってバラバラになりがちです。ルールがないまま放置すると、コメントの品質にムラが出て、結果的に誰もJSDocを信用しなくなるという悪循環に陥ることもあります。
チームでJSDocの規約を策定するときに大切なのは、「書く範囲」を明確にすることです。すべての関数にJSDocを書くのが理想ではありますが、現実的には工数との兼ね合いがあります。おすすめのアプローチは、パブリックなAPIとモジュールのエクスポート部分には必ずJSDocを書き、プライベートな関数やファイル内でしか使わないヘルパー関数は任意とする、というルールです。外部から利用される部分のドキュメントを優先することで、投資対効果を最大化できます。
実は、JSDocの規約を作る際にもうひとつ重要なのが「書く内容の粒度」です。すべてのパラメータに「ユーザーID」「ユーザー名」のような一語の説明しか書かないのであれば、型名を見ればわかる情報と大差ありません。理想的なJSDocは、型情報だけでは伝わらない追加情報を含みます。たとえば「0以上の整数を指定。負の値を渡した場合はエラーになる」「空文字列はデフォルト値として扱われる」といった制約条件や振る舞いの説明です。こうした情報があることで、利用者はソースコードを読まなくても安全にAPIを使えるようになります。
規約テンプレートの作成
チーム内で統一した書き方を浸透させるには、テンプレートを用意するのが効果的です。関数用、クラス用、型定義用など、よく使うパターンごとにテンプレートを作成しておくと、新しいメンバーが参加したときにもスムーズに規約に適応できます。テンプレートはプロジェクトのREADMEやWikiに掲載しておくとよいでしょう。
規約には、使うべきタグと避けるべきタグも明記しておくと混乱を防げます。たとえば、TypeScriptプロジェクトでは @type タグは原則不要です。型は言語レベルで定義されているため、JSDocに二重定義すると保守コストが増えるだけです。一方で @see タグは関連するコードやドキュメントへの参照として有用なので、積極的に使うよう推奨するといった具合です。
そういえば、規約の運用で見落とされがちなのがレビューのプロセスです。JSDocの内容をコードレビューの対象に含めることを明文化しておかないと、「コードは見たけどコメントは読んでいなかった」という状態になりがちです。プルリクエストのチェックリストにJSDocの確認項目を追加するだけでも、ドキュメントの品質を底上げする効果があります。
ドキュメント自動生成との連携
JSDocの大きなメリットのひとつが、コードから直接ドキュメントを生成できることです。TypeDocやapi-extractorといったツールを使えば、JSDocの内容を元にHTMLやMarkdown形式のAPIリファレンスを自動生成できます。コードとドキュメントが常に同期した状態を維持できるため、「ドキュメントが古くて使い物にならない」という問題を根本から解決できます。
TypeDocはTypeScriptプロジェクト向けのドキュメント生成ツールで、JSDocの情報とTypeScriptの型情報の両方を読み取って、見やすいHTMLドキュメントを出力します。設定も比較的シンプルで、npx typedoc --entryPoints src/index.ts --out docs のようなコマンドひとつで生成が完了します。テーマのカスタマイズも可能で、プロジェクトのブランドに合わせたドキュメントサイトを構築できます。
実は、CI/CDパイプラインにドキュメント生成を組み込むことで、デプロイのたびにドキュメントが更新される仕組みを作れます。GitHub ActionsやGitLab CIのワークフローにTypeDocの実行ステップを追加し、生成されたHTMLをGitHub PagesやNetlifyにデプロイする、というのがよくあるパターンです。このフローを一度構築してしまえば、開発者はJSDocをコードに書くだけで、ドキュメントサイトの更新は自動的に行われます。
ところで、Microsoftのapi-extractorは、大規模なTypeScriptプロジェクトやモノレポで特に威力を発揮するツールです。APIの互換性チェックやドキュメント生成に加えて、パブリックAPIのサーフェスを明示的に管理する機能を持っています。ライブラリの開発者にとっては、意図しないAPIの公開を防ぎつつ、高品質なドキュメントを自動生成できるため、非常に心強い選択肢です。
よくある落とし穴と対処法
JSDocをTypeScriptプロジェクトで使い始めると、いくつかの落とし穴に遭遇することがあります。よくある問題を事前に知っておくことで、スムーズに導入を進められるでしょう。
最もありがちな問題は、JSDocの情報と実際のコードの乖離です。関数のパラメータを追加したのにJSDocを更新し忘れる、戻り値の型を変えたのに@returnsの説明がそのまま、といったケースはどのプロジェクトでも起こり得ます。対策としては、先述したeslint-plugin-jsdocの導入が最も効果的です。パラメータの数が一致しない場合にエラーを出す設定にしておけば、不整合を機械的に検出できます。
実は、TypeScriptのstrictモードとJSDocの相性にも注意が必要です。strictモードではnullチェックが厳密に行われますが、JSDocの@paramで「この引数はnullを許容する」と記載していても、型定義側でstring | nullのようになっていなければ意味がありません。ドキュメントと型定義が矛盾していると、利用者を混乱させてしまいます。必ず型定義が正、JSDocはそれを補足する存在、という原則を守ることが大切です。
そういえば、JSDocのMarkdown記法対応について勘違いしている方も少なくありません。JSDocの説明文ではMarkdownを使えますが、すべてのツールが同じように解釈するわけではありません。VSCodeはある程度のMarkdownをレンダリングしてくれますが、TypeDocやapi-extractorではレンダリングの仕方が異なる場合があります。チームで使うツールチェーンに合わせて、どこまでMarkdownを使うか決めておくのが賢明です。
まとめ
JSDocをTypeScriptプロジェクトで活用することは、コードの品質と開発者体験の両方を高める有効な手段です。型情報だけでは伝わらない設計意図や使用上の注意点を、コードのすぐそばに記述できるのがJSDocの最大の強みと言えます。
@paramや@returnsといった基本タグを押さえるところから始めて、@exampleや@deprecated、@throwsで情報を充実させていくのがよいでしょう。VSCodeとの連携を意識してJSDocを書けば、ホバー情報や自動補完を通じて、チーム全体の生産性が向上します。
チーム開発では、書く範囲と粒度を定めた規約を策定し、ESLintによる自動チェックとコードレビューの両面からドキュメントの品質を担保する仕組みを作ることが重要です。TypeDocなどのツールでドキュメント生成を自動化すれば、常に最新の状態が保たれるAPIリファレンスを手に入れられます。JSDocは決して面倒な作業ではなく、未来の自分やチームメンバーへの投資です。今日から一歩ずつ、プロジェクトに取り入れてみてください。