strong-types について
strong-typesスキルは、型付き言語の開発者向けに、型の曖昧さの解消、境界データの型定義、null許容性の明確化を指針として示します。言語別の資料を用意し、既存のチェッカー設定での検証を重視します。
主な機能
- 01関数の引数と戻り値の型指定
- 02境界データへの名前付き型の適用
- 03null許容性と不正状態の明示
- 04型の逃げ道、キャスト、抑制の回避
- 05言語別リファレンスの参照
- 06プロジェクト設定のチェッカーによる検証
活用シーン
- 01型付きコードの実装やリファクタリング
- 02変更箇所のnull許容性と型の曖昧さの確認
- 03外部入力の明確なデータ構造への変換
- 04既存のチェッカー設定での変更内容の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jpcaparas/ skills --skill strong-typesインストール後、Claude Code で/strong-typesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdmetadata.jsonREADME.mdskill-card.prompt.mdagents/openai.yamlassets/.gitkeepevals/evals.jsonreferences/go-rust-swift.mdgotchas.mdgradual-languages.mdjvm-and-dotnet.mdphp.mdprinciples.mdpython.mdreview-rubric.mdsource-notes.mdtypescript.mdscripts/analyze_type_strictness.pytest_skill.pyvalidate.pytemplates/type-review.md詳細
強い型付け
プログラム内のすべての値が、どの時点でも一意にわかる型を持つようにコードを作成、編集、レビューします。型の曖昧さは欠陥です。コードを実行せずに式の型を作者が説明できないなら、そのコードは未完成です。
パッシブトリガー
静的型付け言語(C#、Java、Kotlin、Swift、Go、Rust、TypeScript)または成熟したツールを備えた漸進的型付け言語(PHP、Python、Sorbet を使用する Ruby、型仕様を使用する Elixir)を扱うコーディングタスクでは、常にバックグラウンドでこのスキルを読み込みます。新規コード、編集、リファクタリング、レビュー、実装計画のいずれにも適用します。
適用範囲は相応に保ちます。小さな編集では、型の曖昧さを黙って確認し、実装を変える判断についてのみ言及します。利用可能であれば、一般的なコード品質のために {{ skill:maintainable-code }} も読み込みます。言語やコードベースに使える型システムがない場合は、無理に適用せず、適切な代替手段について references/gradual-languages.md を参照します。
典型的な問題
$image = $location->preview ?? $location->banner ?? $location->thumbnail;
この行には、答えの出ていない疑問が4つ含まれています。各プロパティの型は何か、どれが nullable でその理由は何か、その後の $image の型は何か、そしてチェーン全体が依然として null になりうるか、です。強く型付けされたコードでは、シグネチャですべてに答えます。
final class Location
{
public function primaryImage(): Image
{
return $this->preview ?? $this->banner ?? $this->thumbnail
?? throw new MissingImageException($this->id);
}
}
フォールバックのロジック自体は残っています。ただし、明示的な戻り値の型、すべてが null の場合の明示的なポリシー、各呼び出し箇所でインラインに再導出するのではなくドメインを表す名前(primaryImage)を備えた、名前付きの型付きメソッドにまとめられています。この変換が、このスキルの中核となる手法です。
判断ツリー
何に取り組んでいますか?タスクで言語固有の詳細が必要な場合は、これらのリファレンスを対象を絞った補助資料として使います。すべての編集で必須のチュートリアルとして使うものではありません。フレームワークやインターフェースの型コントラクトは正確に維持し、必要なエスケープハッチは範囲を限定して理由を明確にし、シグネチャに互換性のない制約を加えないようにします。
-
PHP(Laravel、Symfony、標準の PHP): 詳細は
references/php.mdを参照します。declare(strict_types=1)、型付きプロパティ、型付きシグネチャ、連想配列より DTO、PHPStan/Psalm レベルのジェネリクス注釈を必須とします。 -
TypeScript、または TypeScript をコンパイルする JS コードベース: 詳細は
references/typescript.mdを参照します。strict: trueを必須とし、anyと@ts-ignoreを禁止します。境界ではunknownとパースを使い、判別共用体と網羅的な switch を使用します。 -
Python: 詳細は
references/python.mdを参照します。すべてのシグネチャへの注釈、mypy/pyright の strict モード、素の dict より dataclasses/TypedDict/pydantic、文字列よりEnum/Literalを必須とします。 -
C#、Java、Kotlin: 詳細は
references/jvm-and-dotnet.mdを参照します。nullable 参照型(C#)、sealed 階層と record、網羅的なパターンマッチングを必須とし、未検証のキャストと!!を禁止します。 -
Go、Rust、Swift: 詳細は
references/go-rust-swift.mdを参照します。エスケープハッチとしてのinterface{}/any、本番コードパスでのunwrap()、強制アンラップを禁止します。newtype と付随データを持つ enum を優先します。 -
標準の JavaScript、Ruby、Elixir、Lua、シェル、その他の動的型付け環境: 代替手段について
references/gradual-languages.mdを参照します。型付けツールは有用な場合にのみ提案し、プロジェクトを書き換えたり、慣習に逆らったりしないようにします。 -
差分または計画のレビュー: 有用な場合は
references/review-rubric.mdを使います。実行時障害につながる可能性のある曖昧さ、つまり nullable の漏れ、anyによる型情報の隠蔽、文字列型の状態管理、暗黙の型変換を優先して指摘します。 -
どこから始めればよいかわからない場合:
references/principles.mdを読んでから、以下の「型の曖昧さチェックゲート」を実行します。
クイックリファレンス
| 曖昧さの兆候 | デフォルトの対応 |
|---|---|
オブジェクトのプロパティに対する ??/` | |
any、mixed、interface{}、Object、型指定のない dict/array | 具体的な型、ジェネリックパラメーター、ユニオン、または unknown + パースに置き換えます |
| 型指定のない関数シグネチャ | 本体に手を加える前に、引数と戻り値の型を追加します。void/None も型指定に含まれます |
| 境界をまたぐ連想配列/生の dict/匿名オブジェクト | 名前付きの型付きフィールドを持つ DTO、データクラス、レコード、構造体、または TypedDict を定義します |
マジックストリングによる状態("pending"、"active") | enum、シールド型、または網羅的な処理を備えた Literal ユニオンに置き換えます |
| 「念のため」受け渡される nullable 値 | 境界で一度だけ絞り込み、内部では非 null 型のまま受け渡します |
| 外部入力(HTTP、JSON、env、DB の行、CLI 引数) | 境界で型付き構造体にパースし、生のペイロード形状をそのまま受け渡さないようにします |
型エラーの抑制(@ts-ignore、# type: ignore、@phpstan-ignore) | 型を修正するか、具体的なエラーコードと理由をインラインで記述します |
チェッカーを黙らせるために使われるキャスト(as any、(array)、!!、未検証のキャスト) | 絞り込み、型ガード、または明確に失敗するパース処理に置き換えます |
| ステートマシンの状態を表すブール値の引数の組み合わせ | 不正な組み合わせが存在できないよう、1 つの enum またはシールド型に置き換えます |
| 同じ変数に異なる型の値を再代入 | それぞれ 1 つの型を持つ別々の変数に分けます |
| 弱いコンパイラー/チェッカー設定 | 新しいコードは対象範囲を限定して厳格に保ち、レガシーコードへの段階的な適用を行う場合はベースラインを使用します |
コア原則
- すべての関数シグネチャに完全な型を付けます。すべてのパラメーター、戻り値の型、および言語が追跡するエラーのスロー/送出型を含めます。型なしのシグネチャは未完成です。
any、mixed、interface{}、Object、および型を指定しないdict/arrayは、型ではなく回避策です。それぞれの使用には、記述された正当な理由が必要です。基本的には、具体的な型、ジェネリック型、または解析後のunknownを使います。- 不正な状態を表現できないようにします。ブール値、マジックストリング、あらゆるフィールドをオプショナルにしたレコードよりも、列挙型、シールド階層、判別共用体、非 null フィールドを優先します。
- 検証するのではなく、解析します。信頼できない入力は境界で一度だけ型付き構造体に変換し、内部にはその型付き構造体だけを渡します。型を変えずに形状を確認するのは無駄な作業です。
- 関数境界を越えるデータには、名前付きの型を付けます。連想配列、生の dict、匿名の形状は、使うとしてもプライベートなローカル処理に限ります。
- null 許容は設計上の判断であり、デフォルトではありません。フィールドを null 許容にするのは、「存在しない」ことが実際のドメイン状態であり、かつ null 許容値を読み取るたびに、それを解決する担当者が一人だけいる場合に限ります。呼び出し側にフォールバックチェーンがあるのは、担当者が不在であることを意味します。
- 変数の型は一つに保ちます。変数に別の型の値を再代入すると、型推論と読者からの信頼が損なわれます。
- キャストはリスクを移すだけで、なくすわけではありません。型チェッカーが検証できる絞り込み(型ガード、パターンマッチング、
instanceof、網羅的な switch)を優先します。失敗しても明確なエラーにならないキャストは、信じられるのを待つ嘘です。 - 型チェッカーは無効にするのではなく、チェックを強化します。厳格なコンパイラーフラグと厳格なアナライザーレベルを、新規コードの対象範囲における基準とします。レガシーコード全体に導入する場合は、ベースライン/段階的強化用のファイルを使用し、一括抑制は決して使いません。通常の編集で、プロジェクト全体にツールを導入する必要はありません。
- ジェネリック型は、境界を越えて型を保つためにあります。型を消去するコンテナー、リポジトリー、ヘルパー(
List<Object>、Collection<mixed>)を使うと、すべての呼び出し側でキャストが必要になります。代わりに型パラメーターを指定します。 - 網羅性はコメントではなく、コンパイラーによって保証されます。閉じた型に対するすべての switch/match は、すべてのケースを処理する必要があります。ケースが追加されたときにコンパイルエラーにならなければなりません。
- 言語が対応できないことを無理に強制してはいけません。プレーンな JS、ツールを使わない Ruby、Lua、シェルでは、防御的に記述し、データ形状を文書化し、有用な場合は型付けツールを提案するにとどめ、強制しません。代替案については
references/gradual-languages.mdを参照してください。
型の曖昧さに関するゲート
型付き言語で変更を完了する前に、該当するゲートを確認します。これは推論を助けるものであり、レポートですべての行を出力する要件ではありません。
| ゲート | 合格条件 |
|---|---|
| シグネチャ | 新規または編集したすべての関数に、明示的なパラメーター型と戻り値の型がある |
| 回避策 | インラインで正当な理由を示さずに、新たな any/mixed/interface{}/型を指定しないコレクション型を使用していない |
| 境界 | 外部入力が境界で名前付きの型に解析され、生のペイロード形状が内部に渡されない |
| null 許容 | すべての null 許容値に、それを解決する担当者が一人だけいる。呼び出し側に ??/` |
| 状態 | 閉じた状態の集合に列挙型/共用体を使い、コンパイラーが網羅性をチェックする形で処理している |
| キャスト | 型チェッカーを黙らせるだけのキャストがない。代わりに絞り込みまたは解析を使用している |
| 抑制 | 新たな一括エラー抑制がない。対象を絞った抑制には、エラーと理由が明記されている |
| 厳格さ | 新規ファイルがプロジェクトでサポートされる最も厳格なチェッカーレベルを満たしている。段階的導入が対象範囲の場合、レガシーコード向けの段階的強化が設定されている |
| データ形状 | 境界を越えるデータに、連想配列や生の dict ではなく、DTO/レコード/データクラスを使用している |
| 比例性 | 型なし言語のコードを無理に型付きへ変換していない。ガイダンスはあくまで助言にとどめている |
運用ワークフロー
-
型の状況を把握します。 コードを書く前に、言語、チェッカー(tsc、PHPStan、Psalm、mypy、pyright、コンパイラーフラグ)、設定されている厳格さを特定します。その設定と同等以上の厳格さに合わせ、下回らないようにします。
-
まず境界の型を定義します。 ロジックを実装する前に、変更箇所に入るデータと出るデータの DTO、列挙型、シグネチャを定義します。データの形が決まれば、正常系は簡単です。
-
差分の曖昧さを探します。 フォールバックの連鎖、抜け道となる型、型指定のないシグネチャ、マジック文字列、キャストを確認します。それぞれに具体的な型を与えるか、1 行で理由を説明します。
-
null 許容性を発生源で解消します。
?? $b ?? $cの連鎖を見つけたら、各リンクが null 許容である理由を確認し、責任箇所(アクセサー、ファクトリー、データベースのデフォルト、解析ステップ)を特定して、連鎖を名前付きの単一の型付きメンバーにまとめます。 -
チェッカーで検証します。 サポートされているプロジェクト/パッケージの実行方法を使い、設定されたレベルでプロジェクトのチェッカーを実行します。設定を回避する、またはサポートされていない場合は、変更対象のファイルを任意に渡さないでください。新しく追加する範囲を厳格に保ち、既存の失敗や利用できないチェックは、設定を緩めずに報告します。
-
明確に報告します。 重要な型付けや null 許容性に関する判断、検証結果、未解決の曖昧さを報告します。小さな編集で、すべての型やチェック項目を列挙する必要はありません。
任意のヘルパー
ヘルパーは、判断を下すものではなく、曖昧さをすばやく見つけるために使用します。
python3 scripts/analyze_type_strictness.py /path/to/project
python3 scripts/analyze_type_strictness.py /path/to/project --json
フォールバックの連鎖、抜け道となる型、型指定のないシグネチャ、抑制された型エラー、strict_types 宣言の欠如、非厳格な TypeScript 設定を検出します。スキャンで何も検出されなくても、コードが強く型付けされている証明にはなりません。また、多数検出されても、コードに誤りがある証明にはなりません。
ガイダンスが役に立たなくなった場合
同梱された型付けパターンが機能しない場合や、慣用的な書き方と矛盾する場合は、プロジェクトのコンパイラー/チェッカーのバージョンと、言語またはフレームワークの公式ドキュメントを確認します。プロジェクトの設定を緩めずに、最小限の型チェック可能な例を使用します。出典/バージョンと反例を添えて、標準的なスキルの修正または削除を提案します。証拠が得られない場合は、構文をでっち上げず、その旨を報告します。インストール済みのコピーを黙って更新したり、局所的な修正を型付けの移行に拡大したりしないでください。
読み方ガイド
| 必要な情報 | 参照先 |
|---|---|
| あらゆる言語に共通する強い型付けの原則と優れたリファレンス | references/principles.md |
| PHP:strict_types、型付きプロパティ、列挙型、DTO、PHPStan/Psalm のジェネリクス | references/php.md |
| TypeScript:厳格フラグ、any の禁止、ユニオン、境界での解析 | references/typescript.md |
| Python:型注釈、mypy/pyright の厳格モード、データクラス、Protocol | references/python.md |
| C#、Java、Kotlin:null 許容参照、レコード、sealed 型、網羅的なマッチング | references/jvm-and-dotnet.md |
| Go、Rust、Swift:抜け道、ニューボイスタイプ、列挙型、オプショナル | references/go-rust-swift.md |
| 型付けを無理強いしない場合とその方法 | references/gradual-languages.md |
| 型付けに関する差分の重要度優先レビュー | references/review-rubric.md |
| よくある落とし穴とアンチパターン | references/gotchas.md |
| 調査資料と適用上の注意 | references/source-notes.md |
注意点
- 型アノテーションを追加することは、強い型付けと同じではありません。
function f(mixed $x): mixedはアノテーションが付いていても、依然として完全に曖昧です。 - チェッカーを通すためにキャストするのは、チェッカーがないより悪いことです。型システムが誤った内容を積極的に保証してしまいます。
- 境界ごとに DTO を用意することは、関数ごとに DTO を用意することではありません。型を細分化しすぎるとマッピング作業が増えるため、実際に意味を共有するレイヤー間ではドメイン型を再利用してください。
- 厳格化を一気に進めると、後付けの対応は失敗します。段階的に進めましょう。新しいファイルには厳格な設定を適用し、古いファイルは現状のままにして、ベースラインを少しずつ解消してください。
- フォールバックチェーンが隠しているのは、コードの問題ではなくデータの問題かもしれません。スキーマが正規化されていないために画像カラムが 3 つあるのなら、型付きアクセサーは応急処置にすぎません。スキーマの修正を記録してください。
- 信頼できないデータには、
unknown(TS)とobjectが安全なデフォルトです。anyとmixedは安全ではありません。前者はパースを強制しますが、後者はその後のすべてに暗黙のうちに感染します。 - 型をこねくり回すこと自体が、問題の兆候です。ある型を表現するのに条件付きジェネリクスを長々と記述する必要があるなら、型システムを誇示するのではなく、設計をシンプルにしてください。