【TypeScript初心者】コードの解読はもう卒業！コード可読性を劇的に向上させるJSDocとProject Linksの使い方🚀

# 【初心者向け】TypeScript開発でのJSDocとVSCode Project Links活用ガイド


私は`Next.js`と`TailwindCSS`を使用し、フロントエンド開発を行っている初心者です。TypeScript開発において、コードの可読性と保守性の向上に悩んでいました。特に、関数の使用方法を後から見返した時に、理解しづらい状況に直面することが多々ありました。その解決策として見つけたJSDocとVSCode Project Linksの活用方法について、今回はまとめていきます。

## 目次
1. [はじめに](#はじめに)
1. [VSCodeでの基本的な使用](#vscodeでの基本的な使用)
1. [JSDocの基本的な使用例](#jsdocの基本的な使用例)
1. [VSCode Project Linksの詳細](#vscode-project-linksの詳細)
1. [まとめ](#まとめ)


## はじめに
TypeScriptでは、コードに型（データの種類）を指定することができます。例えば「この関数は文字列を受け取って数値を返します」というように、コードの意図を型として記述できます。

しかし、型の情報だけでは「この関数は具体的に何をする関数なのか」「どういう場面で使うのか」といった詳しい説明を残すことができません。また、「このコードに関連する他のファイルはどこにあるのか」を探すのにも時間がかかります。

そこで活用できるのが以下の2つの機能です。

1. **JSDoc**
コードの上部に`/** */`の形式で説明文を書けるコメント機能です。型の情報に加えて、詳しい説明を残すことができます。VSCodeでは、関数にマウスを乗せると、この説明文を見ることができます。

2. **VSCode Project Links**
コメント内に`project://`から始まるパスを書くと、関連ファイルへのリンクを作成できる機能です。クリックで簡単に別ファイルへ移動できます。

本記事では、これらの機能の基本的な使い方から実践的な活用方法まで、具体的な例を交えながら説明していきます。TypeScriptの初心者の方でもすぐに活用できる内容となっています。

## VSCodeでの基本的な使用

JSDocとVSCode Project Linksは、追加の設定なしで以下の基本機能が使えます。

1. JSDocコメントの作成
   - 関数の上で`/**`と入力してEnterを押すと、基本的なJSDocテンプレートが自動生成されます
   - または、下記のカスタムのショートカットを設定して使用できます

2. Project Links
   - JSDocコメント内で`project://`から始まるパスを記述するだけでリンクを作成できます

### キーボードショートカットの設定（推奨）

1. キーボードショートカット設定ファイルを開く

    - Windows/Linux: Ctrl + K Ctrl + S
    - Mac: Cmd + K Cmd + S
    
2. keybindings.json を開く(右上の下記のマークをクリック）
    ![keybindingsJson.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818373/0130180d-e293-2c94-9827-8c0fb52ee6a6.png)

    【右上拡大画像】
    ![keybindingsJsonBig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818373/8cc458f6-1cc8-6969-f4de-e9737db01627.png)

3. 以下の設定を追加

```json
[
    {
        "key": "ctrl+shift+/",
        "command": "editor.action.insertSnippet",
        "args": {
            "snippet": "/** $1 */"
        },
        "when": "editorTextFocus"
    }
]
```

これにより、Ctrl + Shift + /（Macの場合はCmd + Shift + /）を押すことで、JSDocコメントのテンプレートを素早く挿入できます。

## JSDocの基本的な使用例

### 基本的な書き方

コードの直前に`/** */`の形式でコメントを書きます。
基本的な構造は以下の3つの部分からなります。

1. 最初の行に、コードの概要説明を書きます
2. 必要に応じて、詳細な説明を追加します
3. `@`から始まるタグを使って、追加情報を整理します

以下は、ユーザー情報を取得する関数の例です。

```typescript
/**
 * ユーザー情報を表す型
 * @property id - ユーザーを一意に識別するID
 * @property name - ユーザーの表示名
 * @property age - ユーザーの年齢
 */
type UserInfo = {
    id: string;
    name: string;
    age: number;
}

/**
 * ユーザー情報を取得する関数
 * データベースからユーザー情報を検索し、見つからない場合はエラーを投げます。
 * @param userId - ユーザーID
 * @returns ユーザー情報を含むオブジェクト
 * @throws {Error} ユーザーが見つからない場合
 */
const getUser = async (userId: string): Promise<UserInfo> => {
    // 関数の実装
    return {
        id: userId,
        name: "山田太郎",
        age: 30
    };
};
```
この例では、下記のポイントでコメントを記載しています。

- 1行目に関数の概要を書いています
- 2行目に詳細な説明を追加しています
- `@param`、`@returns`、`@throws`の各タグ（JSDocタグ）で補足情報を整理しています

この関数にマウスカーソルを合わせると、書いた説明がポップアップで表示されます。また、この関数を使用する際にも、IDEが説明をサポートとして表示してくれます。

上記の書き方であれば、`UserInfo`をホバーすると、一覧としてプロパティを確認できます。

![型定義前にコメント.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818373/7c820c75-ad43-ca3d-8582-f25acd686e64.png)

しかし、各プロパティをホバーしても説明が表示されません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818373/fae289d1-0f96-d91b-6659-e33cc9fb6741.png)

### プロパティごとの記述方法（推奨）

> 💡 Tips: プロパティごとの記述方法は、@juner さんからご指摘いただいた改善方法です。

その為、下記のように、プロパティごとに書く方法の方がより優れているかと存じます。


```typescript
/**
 * ユーザー情報を表す型
 */
type UserInfo = {
    /** ユーザーを一意に識別するID */
    id: string;
    /** ユーザーの表示名 */
    name: string;
    /** ユーザーの年齢 */
    age: number;
}

```

上記の書き方にすると、UserInfoをホバーしても、一覧としてプロパティの説明文を確認できません。

![型定義途中にコメント1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818373/c7f3b57d-08f8-9ba9-d2e3-fe2b5cddc66d.png)

しかし、各プロパティをホバーすると説明が表示されます。

![型定義途中にコメント2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818373/c24513d1-b53a-5bbe-370b-52aeb87c4de7.png)

プロパティごとに書く方法は以下のメリットがあります。

- より簡潔に書ける
- プロパティとコメントの位置が近いため、コードの意図が分かりやすい
- プロパティ名を変更する際にコメントの修正も忘れにくい
- コードを書いている最中に、必要なプロパティの説明だけをホバーして確認できるので、必要な情報をすぐに確認でき、開発効率が上がる

この方法では各プロパティの直前に /** */ でコメントを記述するだけで、IDEのサポートを最大限活用できます。

### JSDocタグ

コード例の中に@から始まる不思議な記号が出てきて、「これは何だろう？」と思われた方もいるのではないでしょうか。これらはJSDocタグと呼ばれるもので、コードの説明に役立つ特別なマークです。

JSDocでは、コメント内でこの「タグ」を使って、コードの説明を整理していきます。タグは`@`記号で始まり、それぞれが「この部分は引数の説明です」「ここは戻り値の説明です」といった役割を持っています。

#### 主なタグの紹介
表形式で主なJSDocタグを紹介します。

| タグ | 用途 | 説明 | 使用例 |
|------|------|------|--------|
| `@description` | 概要 | 関数やクラスの目的や機能を説明します | `@description ユーザー情報を管理するクラス` |
| `@param` | 関数の引数 | 関数に渡される引数（パラメータ）を説明します | `@param {string} name - ユーザーの名前` |
| `@returns` | 関数の戻り値 | 関数が返す値を説明します | `@returns {boolean} ログイン成功か失敗を返す` |
| `@type` | 型の定義 | 変数やプロパティの型を指定します | `@type {string[]}` |
| `@typedef` | カスタム型定義 | オブジェクトの型や共用型（Union Types）を定義します | `@typedef {Object} UserConfig` |
| `@property` | オブジェクトのプロパティ | `@typedef`や`@type`で定義した型のプロパティを説明します | `@property {string} userName - ユーザー名` |
| `@template` | ジェネリック型 | ジェネリック型パラメータを定義します | `@template T - 配列の要素の型` |
| `@throws` | エラー | 関数で発生する可能性のあるエラーを説明します | `@throws {Error} ユーザーが見つからない場合` |
| `@example` | 使用例 | コードの具体的な使用例を示します | `@example login("user", "pass");` |

TypeScriptでは、型情報は言語自体で表現できますが、JSDocを併用することで下記のようなメリットがあります。
- より詳細な説明を残せる
- チーム内での理解を促進できる
- IDEでのコード補完や型チェックが強化される

マウスホバーで表示される説明文も、これらのタグによってより充実した内容になります。

### JSDocタグの使い分けとTips

#### 状況に応じたタグの選択ガイド

- 関数のドキュメント化
```typescript
/**
 * シンプルな関数の場合：
 * - @param と @returns だけで十分
 */
function add(a: number, b: number): number {
    return a + b;
}

/**
 * 複雑な関数の場合：
 * - @description で詳細な説明
 * - @example で使用例
 * - @throws でエラー条件
 */
function processUserData(data: UserInput): ProcessedData {
    // 実装
}
```

- 型定義のドキュメント化
```typescript
/**
 * シンプルな型の場合：
 * - @type だけで十分
 */
/** @type {string[]} */
const names = ['Alice', 'Bob'];

/**
 * 複雑な型の場合：
 * - @typedef と @property を組み合わせる
 */
/**
 * @typedef {Object} UserConfig
 * @property {string} id - ユーザーID
 * @property {Object} preferences
 * @property {boolean} preferences.darkMode - ダークモード設定
 */
type UserConfig = {
    id: string;
    preferences: {
        darkMode: boolean;
    };
};
```

#### アンチパターン
- 型情報の重複
```typescript
// ❌ 悪い例：TypeScriptの型と重複
/** @type {string} */
const name: string = "John";

// ✅ 良い例：型の追加説明のみ
/** ユーザーの表示名（フルネーム） */
const name: string = "John";
```

- 過剰なドキュメント化
```typescript
// ❌ 悪い例：自明な内容のドキュメント化
/** 数値を1増やす */
function increment(n: number): number {
    return n + 1;
}

// ✅ 良い例：特別な注意点がある場合のみドキュメント化
/** 
 * 数値を1増やす
 * @throws {RangeError} 入力が Number.MAX_SAFE_INTEGER を超える場合
 */
```

- 不適切なタグの使用
```typescript
// ❌ 悪い例：@returns を使うべき場所で @type を使用
/**
 * @type {string}
 */
function getName(): string {
    return "John";
}

// ✅ 良い例：
/**
 * @returns {string} ユーザーの名前
 */
function getName(): string {
    return "John";
}
```

### JSDocの主要なタグと型定義の組み合わせ

#### ジェネリック型との組み合わせ
```typescript
/**
 * データを配列に安全に追加する
 * @template T - 配列の要素の型
 * @param array - 対象の配列
 * @param item - 追加する要素
 * @returns 新しい配列
 */
function addToArray<T>(array: T[], item: T): T[] {
    return [...array, item];
}
```

#### 共用型（Union Types）との組み合わせ
```typescript
/** 
 * ユーザーのアカウント状態を表す型
 * @typedef {('active' | 'inactive' | 'suspended')} UserStatus
 * - active: アクティブなユーザー
 * - inactive: 一時的に無効化されたユーザー
 * - suspended: 運営により停止されたユーザー
 */
type UserStatus = 'active' | 'inactive' | 'suspended';

/**
 * ユーザーの状態を更新する
 * @param userId - ユーザーID
 * @param status - 新しい状態
 * @throws {Error} 無効なステータスの場合
 */
async function updateUserStatus(userId: string, status: UserStatus): Promise<void> {
    // 実装
}
```

## VSCode Project Linksの詳細

### 基本的な使用方法

TypeScriptプロジェクトでのProject Links活用例

```typescript
/**
 * ユーザー管理モジュール
 * 【型定義】
 * project://src/types/user.ts - ユーザー関連の型定義
 * project://src/types/auth.ts - 認証関連の型定義
 * 
 * 【実装】
 * project://src/services/userService.ts - ユーザーサービスの実装
 * project://src/utils/validation.ts - バリデーション関連
 * 
 * 【テスト】
 * project://tests/services/userService.test.ts
 */
```

### 使用シーン別の記述例

#### 1. Next.jsのページコンポーネント
```typescript
/**
 * ユーザープロフィールページ
 * 【コンポーネント構成】
 * project://src/components/Profile/ProfileHeader.tsx
 * project://src/components/Profile/ProfileDetails.tsx
 * 
 * 【API連携】
 * project://src/pages/api/users/[id].ts
 * 
 * 【状態管理】
 * project://src/stores/user/profileStore.ts
 * project://src/stores/user/types.ts
 */
const UserProfilePage: NextPage = () => {
    // 実装
}
```

#### 2. カスタムフック
```typescript
/**
 * ユーザーデータを管理するカスタムフック
 * 【関連ファイル】
 * project://src/hooks/useAuth.ts - 認証フック
 * project://src/hooks/useForm.ts - フォーム管理
 * project://src/api/userApi.ts - APIクライアント
 * 
 * 【型定義】
 * project://src/types/hooks.ts - フック用型定義
 * project://src/types/api.ts - API関連の型定義
 */
const useUser = () => {
    // 実装
}
```

### Project Linksの制限事項と注意点

#### パスの制限

- **相対パスの非対応**
```typescript
/**
 * ❌ 動作しない例：
 * ../components/Header.tsx
 * ./utils/helper.ts
 * 
 * ✅ 正しい例：
 * project://src/components/Header.tsx
 * project://src/utils/helper.ts
 */
```

- 大文字小文字の区別
```typescript
/**
 * ❌ 避けるべき例：
 * project://src/Components/Header.tsx
 * 
 * ✅ 推奨される例：
 * project://src/components/header.tsx
 */
```

#### 注意点
- VSCode以外のエディタでは機能しません
- ファイルパスを変更した場合、すべてのProject Linksを手動で更新する必要があります



## まとめ

TypeScript環境においても、JSDocとVSCode Project Linksは非常に有用なツールです。TypeScriptの型システムと組み合わせることで、より詳細なドキュメント化が可能になり、コードの理解性と保守性が向上します。また、VSCode Project Linksを活用することで、大規模なTypeScriptプロジェクトでもファイル間の関連性を明確に管理できると存じます。

もし記事の内容に間違いがあれば、コメントでご指摘いただけますと幸いです。また、より良い方法や代替手段をご存知の方がいらっしゃいましたら、ぜひ共有していただければと存じます。例えば、型定義とJSDocの使い分け、大規模プロジェクトでのドキュメント管理の工夫など、皆様の実践的な知見をお待ちしています。