型ヒントとmypy活用:レビューで補強すべき設計思想の確認点

Pythonのtype hintmypyによる静的型チェックは、コードの安全性と可読性を大きく向上させる手段である。
しかし、記法が正しくても設計として正しいとは限らない。型ヒントは「書く」ものではなく「伝える」ものであり、レビューアーはその型が何を意図しているかを読み取る立場にある。

本マニュアルでは、型ヒントとmypy活用が組織内で定着するために、レビュー時に押さえるべき設計思想と判断ポイントを体系的に解説する。

型ヒントの目的を再確認する

型ヒントの役割は以下の通り。

  • 静的解析ツールでのエラー検出
  • 補完・ドキュメントの自動生成
  • 関数仕様の明示
  • 依存注入や抽象設計との整合性

レビューでは「書かれているか」ではなく、「設計の表現手段として適切か」を評価する。

def process(data: dict) -> list:
    ...
設計意図レビュー
@Reviewer: `dict`, `list` だけの型指定では、データ構造の仕様が読み取れません。キーや値の型を明示し、より具体的な構造定義へ更新してください。
mypyとは

mypy は Python の型ヒント(PEP484)に基づいて、静的にコードの型整合性をチェックするツール。
補完や構文検証では検出できない「仕様の矛盾」を事前に洗い出すことができる。

よくあるレビュー対象の型設計ミス

ミス1:Anyを多用しすぎて意味を失っている

def process(input: Any) -> Any:
    ...
Comment
@Reviewer: `Any`は型チェックの回避であり、型ヒントとしての意図が失われます。具体的に想定する型を定義・制約するようにしてください。

ミス2:OptionalとUnionの使い分けが曖昧

def get_status(flag: bool) -> Optional[str]:
    if flag:
        return "ok"
    return None

Optional[str] で正しいが、呼び出し側がNoneを考慮すべきか明示されていない場合はレビュー対象。

Comment
@Reviewer: `Optional[str]`の使用意図(None許容理由)が関数ドキュメントや戻り値制御から読み取れません。設計上の意図を明確にしてください。

ミス3:構造体の型がdict[str, Any]のまま

def get_user() -> dict[str, Any]:
    return {"id": 1, "name": "Taro"}

改善:TypedDictまたはdataclassの導入

class User(TypedDict):
    id: int
    name: str

def get_user() -> User:
    ...
Comment
@Reviewer: dict[str, Any] は型チェックの恩恵を受けにくく、構造も曖昧です。TypedDictや明示構造を定義し、コード全体の一貫性を確保してください。

型ヒントがない場合のリスク構造図

UML Diagram

mypyと連携して確認すべきレビュー観点

観点 確認内容 優先度
関数・クラスの型注釈 引数・戻り値がすべて型注釈されているか
Anyの局所利用 一時的な型バイパスに限定されているか
型不整合の警告抑制有無 # type: ignore を適切に使用し、その理由が明示されているか
ジェネリクスの正当利用 List[T]やMapping[str, V]等、型パラメータが有効に機能しているか
mypyのstrict設定 --strict, --disallow-untyped-defs 等をCIに組み込んでいるか

テストと型ヒントの一致を確認する

テストコード側でのassert文やfixture構造に対して、関数側の型ヒントと整合しているかをレビューする。

def parse(json_str: str) -> dict[str, str]:
    return json.loads(json_str)
# テスト側で期待する型が list[str] の場合、食い違いが発生
def test_parse():
    result = parse("[]")
    assert isinstance(result, list)
Comment
@Reviewer: 戻り値型 `dict[str, str]` がテストで使用されている構造と一致していません。実装・型ヒント・テストの整合性を確認してください。

型ヒント設計の補助構造:Literal, Final, Protocol

Literal(リテラル値の限定)

def set_mode(mode: Literal["debug", "release"]) -> None:
    ...
Comment
@Reviewer: 許容値がコード上で明示されており、設計意図が伝わりやすくなっています。適切な使用です。

Final(再代入禁止)

from typing import Final
TIMEOUT: Final = 5
Comment
@Reviewer: 設定値が定数として宣言されており、再代入禁止によって意図が明確です。良い構造です。

Protocol(構造的サブタイピング)

class Cache(Protocol):
    def get(self, key: str) -> str: ...

レビューでは既存の具象型に依存せず抽象性を維持するための設計判断として活用を評価する。

結論:レビューアーは「型の記法」ではなく「設計意図」を問うべきである

Pythonの型ヒントは、書かれていれば良いというものではない。
レビューアーの役割は、「その型が何を表し、なぜそう定義されたのか」「その型でどんな誤解を防ごうとしているのか」を読み解き、設計意図に沿った安全なインタフェースになっているかを確認することである。

静的型チェックは、設計思想と実装品質の中間地点を可視化する強力なツールであり、レビューアーはその橋渡しを担う存在である。