mcpField
B
ban-type-assertions

ban-type-assertions

github.com/factory-ai開発117144 回インストール3 日前に更新
リポジトリ

ban-type-assertions について

TypeScriptの型アサーションを禁止するための開発スキルです。ESLintルールを有効にし、`as`キャストをコンパイラーが検証できる方法に置き換えます。データ境界でのスキーマ検証や、関連する型、テスト、パッケージの確認も案内します。

主な機能

  1. 01型アサーション禁止のESLintルール設定
  2. 02Zodによる境界データの解析
  3. 03制御フローによるユニオン型の絞り込み
  4. 04共有スキーマの確認と統合
  5. 05lint、型チェック、テストの実行

活用シーン

  1. 01TypeScriptプロジェクトから`as`キャストを除去
  2. 02JSON、ディスク、ネットワーク由来のデータ検証
  3. 03アサーションルール導入後のテスト修正

インストール

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

$npx skills add https://github.com/factory-ai/factory-plugins --skill ban-type-assertions

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

詳細

型アサーションを禁止する

パッケージで assertionStyle: 'never' を設定して @typescript-eslint/consistent-type-assertions を有効にし、すべての as X キャストをコンパイラーが検証できるパターンに置き換えます。

基本方針

より簡単な方法ではなく、厳密に正しい方法を選びます。

すべての as アサーションは、開発者がコンパイラーに「信じてください」と伝える箇所です。目指すのは、代わりにコンパイラーに検証させることです。as Foo を同じく未検証の型ガードに置き換えても、何も改善していません。アサーションの場所を移しただけです。

クイックリファレンス

  • ルール:@typescript-eslint/consistent-type-assertions
  • 設定:{ assertionStyle: 'never' }
  • 場所:packages/<name>/.eslintrc.js

ワークフロー

1. ルールを有効にする

パッケージの .eslintrc.js に追加します。

rules: {
  '@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }],
}
2. 違反箇所を列挙する
cd packages/<name> && npm run lint 2>&1 | grep "consistent-type-assertions"

修正する前に、ファイルとパターンごとに違反箇所を分類します。

3. 修正前に調査する

置き換えコードを書く前に、以下を確認します。

  1. 既存の zod スキーマを確認する — @factory/common とリポジトリ全体で、型名と併せて Schema を検索します。
  2. スキーマが存在してもエクスポートされていない場合は確認する — その場合は、新しく作成するのではなくエクスポートします。
  3. パッケージ間で重複した型/インターフェースを確認する — 見つかった場合は、@factory/common に統合します。
  4. データフローを把握する — これはパース境界(外部データ)、型の絞り込み箇所(ユニオン型)、それともライブラリの型の不足でしょうか。
4. パターンの優先順位に従って違反を修正する
優先度 1:Zod によるパース(外部データとの境界向け)

JSON、ディスク、ネットワーク、IPC などからシステムに入るデータには、すべてこれを使用します。型注釈だけでなく、実行時検証を行えます。

// BAD
const data = JSON.parse(raw) as MyType;

// GOOD
const data = MySchema.parse(JSON.parse(raw));

エラーを適切に処理する必要がある場合(リクエスト ID などのコンテキストを含むエラーレスポンスを返す場合など)は、safeParse を使用します。

// BAD: throws before you can extract the request id
const request = RequestSchema.parse(JSON.parse(raw));

// GOOD: safeParse lets you return a proper error
const parsed = RequestSchema.safeParse(JSON.parse(raw));
if (!parsed.success) {
  return errorResponse(rawObj?.id ?? null, INVALID_PARAMS, parsed.error.message);
}
const request = parsed.data;
優先度 2:制御フローによる型の絞り込み(ユニオン型向け)

switch、in、instanceof、または判別可能なユニオンを使用します。

// BAD
(error as NodeJS.ErrnoException).code

// GOOD
if (error instanceof Error && 'code' in error) {
  const code = error.code;
}
// BAD
if (METHODS.has(method as Method)) { ... }

// GOOD: switch narrows exhaustively
switch (method) {
  case 'foo':
  case 'bar':
    return handle(method); // narrowed
}
優先度 3:根拠を示した eslint-disable(最後の手段)

本当に避けられないケース(ライブラリの型の不足、推論できないジェネリック型パラメーターなど)に限ります。必ず理由を説明します。

// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- ws library types require generic parameter
ws.on('message', handler);
アンチパターン:アサーションを偽装した型ガード
// NOT an improvement -- checks shape but not content
function isDaemonRequest(x: unknown): x is DaemonRequest {
  return typeof x === 'object' && x !== null && 'method' in x;
}

Zod スキーマは値を検証します。このような型ガードは、手順を増やしただけの未検証アサーションです。絞り込みのロジックが本当に十分な場合に限り、型ガードを使用します。

5. 寛容なスキーマではなく、厳密なスキーマを使用する

スキーマ(例:SessionSettingsSchema)が存在する場合は、z.record(z.unknown()) ではなく、そのスキーマを厳密に使用します。これにより、前方互換性が保たれます。マイグレーションでフィールドが削除されていれば、読み込み時に古いデータがクリーンアップされます。

// BAD: accepts anything
const settings = z.record(z.unknown()).parse(raw);

// GOOD: validates against the real shape
const settings = SessionSettingsSchema.parse(raw);
6. 共有スキーマを @factory/common に昇格する

パッケージ間で重複したインターフェース、型、スキーマを見つけた場合は、統合します。

  1. @factory/common/<domain>/<subdomain>/schema.ts にスキーマを作成します
  2. 列挙型は兄弟ファイルの enums.ts に配置します(factory/enum-file-organization により必須)
  3. index.ts のバレルではなく、サブパス(例:@factory/common/session/summary)経由でエクスポートします
  4. ローカルにある重複定義をすべて削除します
  5. すべての利用側を更新し、共通のサブパスからインポートするようにします
  6. リポジトリのルートで npm run knip を実行し、未使用のバレル再エクスポートを検出します
7. テスト用モックをスキーマに合わせて修正する

as X を .parse() に置き換えると、アサーションに依存していたテスト用モックは検証に失敗します。モックを修正してください。テストではルールを無効にしないでください。

有効なテスト用フィクスチャを一元管理するヘルパー関数を作成します。

function mockSessionSummary(
  overrides?: Partial<SessionSummaryEvent>,
): SessionSummaryEvent {
  return {
    type: 'session_start',
    id: 'test-id',
    title: 'Test Session',
    owner: 'test-owner',
    ...overrides,
  };
}
8. エラー処理の中で、境界部分でパースする

パースの失敗が未処理の例外ではなく、適切なエラーレスポンスにつながる場所でパースが行われるようにします。

// BAD: parse outside try/catch -- if it throws, you lose context
const request = RequestSchema.parse(data);
try { handle(request); } catch { ... }

// GOOD: safeParse before try, handle error with context
const parsed = RequestSchema.safeParse(data);
if (!parsed.success) {
  return errorResponse(rawData?.id ?? null, INVALID_PARAMS, parsed.error.message);
}
try { handle(parsed.data); } catch { ... }

検証

影響を受けるすべてのパッケージを対象に実行します(@factory/common の変更が、下流のパッケージの lint を失敗させる可能性があります)。

# Lint (all affected packages)
cd packages/<name> && npm run lint

# Typecheck
npm run typecheck

# Tests
npm run test

# Unused exports (repo root)
npm run knip

注意事項

  • factory/enum-file-organization では、TypeScript の列挙型を enums.ts という名前のファイルに配置する必要があります
  • no-barrel-files は、バレルファイルから型を再エクスポートすることを防ぎます。利用側はサブパスから直接インポートする必要があります
  • 型を common に移す際、新しいサブパスの package.json エクスポートエントリーが存在しない場合は追加してください
  • テストファイルでモックのセットアップにアサーション構文を使用している場合、.eslintrc.js でこのルールのテスト用例外設定が必要になることがあります。ただし、ルールを無効にするより、モックを修正することを優先してください

コメント