Skip to content

refactor: createScanAction.ts の api signature を ScanApi<TArgs, TResult> 型として export #255

Description

@ymnao

動機

PR #251 (createScanAction.test.ts の vi.fn() typing 統一) の /simplify pass 2 副産物として発見した refactor 候補。

src/stores/createScanAction.tsCreateScanActionOptions.api field は () => (...args: TArgs) => Promise<TResult> という inline thunked signature を持つ。これを ScanApi<TArgs, TResult> という named type alias として切り出し export することで、

  • 型の意図を docstring 付きで明示できる (現在の api field 上の長文 doc comment は型の概念ではなく field の使い方を説明している)
  • 将来 scan 系 helper (別 store factory / mocks utility 等) を増やした際の参照点を確保できる
  • caller サイト (src/stores/backlink.ts / src/stores/wikilink.ts) / test mock で type assertion / satisfies に使える余地を残す

現状

src/stores/createScanAction.ts:6-21:

interface CreateScanActionOptions<TState extends ScanStateBase, TArgs extends unknown[], TResult> {
	/**
	 * scan 実行時に毎回呼ばれる thunk。中で実 API 関数を返す。
	 *
	 * thunk にしているのは store 評価時に `commands` からシンボルを直接読み出すと、
	 * 関係ない hooks/components のテストが `vi.mock("../lib/commands", () => ({ ... }))` で
	 * 一部の export しか mock していない場合に strict-mock エラーが出るのを避けるため。
	 * thunk 内の `commands` への参照は ES module の live binding で scan 呼び出し時に解決される。
	 */
	api: () => (...args: TArgs) => Promise<TResult>;
	// ...
}

caller サイト:

  • src/stores/backlink.ts:23: api: () => scanBacklinks
  • src/stores/wikilink.ts:59: api: () => scanUnresolvedWikilinks

caller は createScanAction<TState, TArgs, TResult>(...) の generic で TArgs / TResult を渡しており、api field の型は generic 推論で互換性確保済。

修正案

Option A (推奨): createScanAction.ts のみ変更

/**
 * scan 実行時に毎回呼ばれる thunk。中で実 API 関数を返す。
 *
 * thunk にしているのは ...(既存 doc を ScanApi 側へ移動)
 */
export type ScanApi<TArgs extends unknown[], TResult> = () => (
	...args: TArgs
) => Promise<TResult>;

interface CreateScanActionOptions<TState extends ScanStateBase, TArgs extends unknown[], TResult> {
	api: ScanApi<TArgs, TResult>;
	// ...
}
  • doc comment は ScanApi 側に集約 (型 alias 自身の意味を説明する役割が適切)
  • caller / test 変更不要
  • Diff: 1 file、+5 -1 程度

Option B: caller サイトでも ScanApi 注釈を強制

// src/stores/backlink.ts
import { createScanAction, type ScanApi } from "./createScanAction";

const backlinkScanApi: ScanApi<[string, string], BacklinkSource[]> = () => scanBacklinks;

scan: createScanAction<BacklinkState, [string, string], BacklinkSource[]>({
	api: backlinkScanApi,
	// ...
}),
  • caller サイトで type alias を明示活用
  • scanBacklinks signature 変更時に caller variable レベルでも検出
  • Diff: 3 file (createScanAction.ts / backlink.ts / wikilink.ts)、+15 -3 程度

Option A を推奨する根拠

  1. createScanAction<TState, TArgs, TResult> の generic で既に caller サイトの型推論が機能しており、api: () => scanBacklinks の inline 形式が () => (...args: TArgs) => Promise<TResult> と整合するか型レベルで検証される。Option B の variable 注釈は冗長
  2. caller サイトの可読性 (inline api: () => scanBacklinks の宣言的さを維持)
  3. scope 最小化。将来 ScanApi を他箇所で活用する必要が生じた時に Option B へ漸進的に拡張可能

影響範囲

  • src/stores/createScanAction.ts: +5 -1 (type alias 追加 + api field 型置換、doc comment の移動)

caller / test は変更なし:

  • src/stores/backlink.ts: 変更なし
  • src/stores/wikilink.ts: 変更なし
  • src/stores/createScanAction.test.ts: 変更なし

Tier 推定

Tier B (defect-detection 強化、impact 小〜中、scope 小)

  • 既存の generic 型推論で defect-detection は既に機能しているため、本 PR の直接 impact は「将来 helper 追加時の参照点確保」「型の意図文書化」が主
  • scope は最小変更 (Option A) で 1 file +5 -1 程度

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions