mcpField
M
method-shorthand-jsdoc

method-shorthand-jsdoc

github.com/epicenterhq開発4.8k56 回インストール2 週間前に更新
リポジトリ

method-shorthand-jsdoc について

TypeScript のファクトリー関数を構成し、返却メソッドの JSDoc を利用側から見えるようにするスキルです。返却オブジェクトのメソッドだけが使うヘルパーには、メソッド簡略記法を用います。

主な機能

  1. 01返却メソッドの JSDoc を保持
  2. 02返却オブジェクトでメソッド簡略記法を使用
  3. 03ヘルパーの配置方法を提示
  4. 04this による同階層メソッド呼び出しを説明

活用シーン

  1. 01TypeScript ファクトリー API の設計
  2. 02IDE のホバードキュメントの維持
  3. 03プライベートヘルパーとメソッドの選択

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/epicenterhq/epicenter --skill method-shorthand-jsdoc

インストール後、Claude Code で/method-shorthand-jsdocを入力して読み込まれたか確認します。

詳細

JSDoc を保持するためのメソッド省略記法

ファクトリー関数に、返されるメソッドからのみ使用されるヘルパー関数がある場合は、メソッド省略記法を使ってそのヘルパーを返却オブジェクトの中に移動します。これにより、JSDoc コメントが利用側に正しく引き継がれます。

関連スキル:4 つのゾーンからなるファクトリーの構成と this の判断ルールについては、factory-function-composition を参照してください。

問題

ドキュメントが充実したヘルパーを持つファクトリー関数を書くとします。

function createHeadDoc(options: { workspaceId: string }) {
	const { workspaceId } = options;

	/**
	 * Get the current epoch number.
	 *
	 * Computes the maximum of all client-proposed epochs.
	 * This ensures concurrent bumps converge to the same version.
	 *
	 * @returns The current epoch (0 if no bumps have occurred)
	 */
	function getEpoch(): number {
		let max = 0;
		for (const value of epochsMap.values()) {
			max = Math.max(max, value);
		}
		return max;
	}

	return {
		workspaceId,
		getEpoch, // JSDoc is NOT visible when hovering on returned object!

		bumpEpoch(): number {
			const next = getEpoch() + 1; // Calling internal helper
			return next;
		},
	};
}

IDE で head.getEpoch() にカーソルを合わせても、何も表示されません。JSDoc が失われています。

解決策

メソッド省略記法を使って、ヘルパーを返却オブジェクトの中に移動します。

function createHeadDoc(options: { workspaceId: string }) {
	const { workspaceId } = options;

	return {
		workspaceId,

		/**
		 * Get the current epoch number.
		 *
		 * Computes the maximum of all client-proposed epochs.
		 * This ensures concurrent bumps converge to the same version.
		 *
		 * @returns The current epoch (0 if no bumps have occurred)
		 */
		getEpoch(): number {
			let max = 0;
			for (const value of epochsMap.values()) {
				max = Math.max(max, value);
			}
			return max;
		},

		bumpEpoch(): number {
			const next = this.getEpoch() + 1; // Use this.methodName()
			return next;
		},
	};
}

これで head.getEpoch() にカーソルを合わせると、JSDoc 全体が表示されます。

公開型をファクトリーから導出する場合は、さらに重要です。

export type HeadDoc = ReturnType<typeof createHeadDoc>;

ReturnType では、返却オブジェクトが公開型のソースになります。利用者向けの JSDoc は、返却されるメソッドまたはゲッターに直接記述してください。そうすれば、ホバー表示、補完、定義へ移動のすべてが同じメンバーを参照します。

JSDoc の保持と定義へ移動の流れは、どちらも利用者のツールがどこを参照するかという同じ選択から生じます。ゾーン 4 でメソッド省略記法を使うと、どちらも実際の定義を参照します。手書きのインターフェース注釈、モジュールレベルのオブジェクトを分割代入して再エクスポートする方法、あるいは関数をそのまま再出力するだけのラッパーは、両者を分断する問題を引き起こします。ナビゲーションに関するルールについては、typescript の「定義へ移動の認識」を参照してください。

これが機能する理由

  1. JSDoc はメソッドの定義箇所に付与されます - メソッドを返却オブジェクト内に直接記述すると、JSDoc は TypeScript が認識するプロパティに直接付与されます
  2. メソッド省略記法は function と同じセマンティクスを使います - this はオブジェクトに束縛されるため、this.getEpoch() が機能します
  3. 別のヘルパーは不要です - 同じオブジェクト内の他のメソッドからのみ使用されるなら、そのオブジェクトに置くべきです

パターン

// BAD: Helper defined separately, JSDoc lost on return
function createService(client) {
  /** Fetches user data with caching. */
  function fetchUser(id: string) { ... }

  return {
    fetchUser,  // JSDoc not visible to consumers!
    getProfile(id: string) {
      return fetchUser(id);  // Works, but consumers can't see docs
    },
  };
}

// GOOD: Method shorthand, JSDoc preserved
function createService(client) {
  return {
    /** Fetches user data with caching. */
    fetchUser(id: string) { ... },

    getProfile(id: string) {
      return this.fetchUser(id);  // Use this.method()
    },
  };
}

判断ルール

ヘルパーが返却されるメソッドからのみ使用され、利用者がそのメソッドのホバー表示で JSDoc を必要とし、初期化中には実行されない場合は、返却オブジェクト内に移動します。

return の前に呼び出される場合、複数のファクトリー間で共有される場合、または公開されない真に内部的な処理である場合は、ヘルパーを分離したままにします。

アロー関数では機能しません

アロー関数は独自の this を持ちません。

// BAD: Arrow function, this is undefined
return {
  getEpoch: () => { ... },
  bumpEpoch: () => {
    this.getEpoch();  // ERROR: this is undefined!
  },
};

// GOOD: Method shorthand has correct this binding
return {
  getEpoch() { ... },
  bumpEpoch() {
    this.getEpoch();  // Works!
  },
};

実例

packages/epicenter/src/core/docs/head-doc.ts より:

export function createHeadDoc(options: { workspaceId: string; ydoc?: Y.Doc }) {
	const { workspaceId } = options;
	const ydoc = options.ydoc ?? new Y.Doc({ guid: workspaceId });
	const epochsMap = ydoc.getMap<number>('epochs');

	return {
		ydoc,
		workspaceId,

		/**
		 * Get the current epoch number.
		 *
		 * Computes the maximum of all client-proposed epochs.
		 * This ensures concurrent bumps converge to the same version
		 * without skipping epoch numbers.
		 *
		 * @returns The current epoch (0 if no bumps have occurred)
		 */
		getEpoch(): number {
			let max = 0;
			for (const value of epochsMap.values()) {
				max = Math.max(max, value);
			}
			return max;
		},

		/**
		 * Bump the epoch to the next version.
		 *
		 * @returns The new epoch number after bumping
		 */
		bumpEpoch(): number {
			const next = this.getEpoch() + 1;
			epochsMap.set(ydoc.clientID.toString(), next);
			return next;
		},

		// ... other methods using this.getEpoch()
	};
}

まとめ

方法JSDoc が表示されるかthis が機能するか
分離したヘルパー+参照いいえ該当なし
返却オブジェクト内のアロー関数はいいいえ
返却オブジェクト内のメソッド省略記法はいはい

メソッド省略記法だけが、JSDoc を保持し、さらに this を使ってメソッド同士を呼び出せる方法です。

ファクトリー関数の構成における位置づけ

ファクトリー関数の内部構造は、4 つのゾーンで構成されます。不変状態 → 可変状態 → プライベートヘルパー → 返却オブジェクト。メソッド省略記法はゾーン 4(返却オブジェクト)、つまり公開 API に置きます。

this.method() を使うか直接呼び出すかの判断は、その関数がどのゾーンにあるかによって決まります。

状況配置場所呼び出し方
返却オブジェクト内の同じ階層のメソッドからのみ使用されるゾーン 4(返却オブジェクト、メソッド省略記法)this.method()
返却オブジェクトのメソッドと、return 前の初期化処理の両方から使用されるゾーン 3(プライベートヘルパー、独立した関数)直接呼び出し:helperFn()
初期化中にのみ使用され、公開されないゾーン 3(プライベートヘルパー)直接呼び出し:helperFn()

ヘルパーをゾーン 3 に置く必要がある場合、その JSDoc は利用者には表示されません。これは正しい動作です。これはプライベートな実装詳細だからです。利用者向けの JSDoc が必要なのは、ゾーン 4 のメソッドだけです。

ファクトリー関数の構成全体については、Closures Are Better Privacy Than Keywords を参照してください。

参考資料

コメント