principle-type-system-discipline について
安全な型モデルを目指す開発者向けの型システムの原則をまとめたスキルです。有効な状態を型で表し、境界でデータを解析し、コンパイラーで未処理のケースを検出する方針を示します。
主な機能
- 01型で不正な状態を表現不能にする
- 02有効な値から型を構築
- 03意味の異なるプリミティブにブランド型を使う
- 04境界で外部データを解析
- 05すべてのバリアントを網羅的に検査
- 06信頼できるスキーマから型を導出
活用シーン
- 01相互排他的な状態のモデル化
- 02異なる ID 型の混同防止
- 03JSON など外部入力の検証
- 04変更後の未処理バリアントの検出
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ q-qp-p/ pstack --skill principle-type-system-disciplineインストール後、Claude Code で/principle-type-system-disciplineを入力して読み込まれたか確認します。
詳細
型システムの規律
コンパイラーに、あり得ない状態、プリミティブ型の不一致、未処理のバリアントを拒否させます。ハンドラーを増やすより、エラーや特殊ケースそのものをなくすことを優先します。TypeScript の構文については、typescript-best-practices スキルを参照してください。
- 不正な状態を表現できないようにします。 バリアントは、矛盾する組み合わせでもコンパイルできてしまう省略可能なフィールドの寄せ集めではなく、直和型(判別共用体、ペイロード付き列挙型、シールドクラス)としてモデル化します。
{ completed: boolean; completedAt?: Date }では、日付がないcompleted: trueが許されます。ブール値をcompletedAtから導出するか、{ kind: 'open' } | { kind: 'done'; at: Date }のようにモデル化します。フィールドの組み合わせが有効となる条件をコメントで説明できるなら、直和型に分割してください。 - 型は制約ではなく構成です。 より緩い型をチェックで削り出すのではなく、必要な値から型を組み立てます。空でないリストは、先頭要素と残りの要素で構成されます。有効な時間範囲は、開始時刻と継続時間で構成されます。不正な値を構築できない形を選び、その上に呼び出し側が必要とするインターフェースを公開します。
- 意味を持つプリミティブ型にはブランドを付けます。
UserIdとOrderIdは内部的にはどちらも文字列ですが、相互に置き換えられないようにすべきです。それぞれに、TypeScript のブランド付き交差型や、ほかの言語での新しい型など、独自の型を与えます。生成時に一度検証し、以降はその型を信頼します。2 つの引数が同じプリミティブ型でも意味が異なるなら、ブランドを付けてください。 - 外部データはパースされるまで型なしです。 RPC ペイロード、JSON、IPC メッセージ、CLI 引数、設定ファイル、環境変数、データベースの行などが対象です。構造化されていない入力を型付きモデルに変換するパース関数を、すべての境界に用意します。boundary-discipline 原則スキルでは、検証をどこに置くべきかを説明しています。
- 型システムに偽りを言わないでください。 コンパイラーを迂回するキャスト、安全でない型変換、アサーション関数は、潜在的な実行時クラッシュにつながります。コンパイラーが事実を証明できないなら、検証、絞り込み、モデルの精緻化によって証明するか、キャストが危険であることを受け入れます。それぞれの
any、as、assertNotNullを境界までたどり、そこで検証してください。 - 網羅的なマッチングはコンパイラーの役目です。 直和型のマッチは、新しいバリアントが追加されたのに処理されていない場合、コンパイルに失敗する必要があります。そうすればコンパイラーが、次のエージェントにケースを追加すべき場所を知らせてくれます。各言語の慣用的な方法を使います。TypeScript では
never型のバインディング、Rust では注釈なしのmatch、Haskell では-Wincomplete-patterns、Kotlin ではシールドクラスのマッチ網羅性です。 - 信頼できるスキーマから型を導出します。 プロトコルバッファー、OpenAPI 仕様、GraphQL スキーマ、データベースマイグレーション、デザイントークンファイルなど、ほかのファイルがデータ形状を定義している場合は、並行する型を手作業で作るのではなく、そこから導出します。encode-lessons-in-structure 原則スキルを参照してください。
- 部分性が現れる箇所でのみ型を強化します。 実行時アサーション、null チェック、「ここには到達しないはず」という例外は、型が弱すぎる場所を示しています。そのチェックを型に反映してください。そこで止めます。型システムの役割は、データを可能な限り正確に記述することではなく、各使用箇所が処理すべきケースを追跡することです。全域関数を優先してください。空リストの
sumは 0 なので、通常のリストを受け取ります。空リストのheadには答えがないため、空でないリストを要求します。ほかの方法ではパニックが起きないなら、通常の型をそのまま使ってください。
コメント
Markdown 対応