UserId = int のように変数代入で型エイリアスを表現。typing モジュールのインポートは不要だが、意図が曖昧。
型ヒントが複雑になってくると、同じ型を何度も書く場面が増えてくる。dict[str, list[tuple[int, int]]] のような型を関数ごとに繰り返し書くのは読みにくいし、変更があったときに修正箇所が散らばってしまう。TypeAlias を使えば、複雑な型に分かりやすい名前を付けて一元管理できる。
単純な型エイリアス
最もシンプルな型エイリアスは、変数に型を代入するだけで作れる。
UserId = int Username = str def find_user(uid: UserId) -> Username | None: users: dict[UserId, Username] = {1: "Alice", 2: "Bob"} return users.get(uid)
UserId や Username は int や str の別名にすぎないが、コードの意図が格段に伝わりやすくなる。ただし、この書き方だと型チェッカが「これは型エイリアスなのか、それとも普通の変数なのか」を判断しにくいことがある。
TypeAlias による明示的な宣言
Python 3.10 で導入された TypeAlias を使えば、「これは型エイリアスである」と明示できる。
from typing import TypeAlias UserId: TypeAlias = int ScoreList: TypeAlias = list[int] UserScores: TypeAlias = dict[str, ScoreList]
UserId = int — 型エイリアスか変数代入か、見た目では区別しにくい。
UserId: TypeAlias = int — 型エイリアスであることが一目でわかる。型チェッカも正確に解釈する。
TypeAlias を使うことで、コードを読む人にも型チェッカにも意図が正確に伝わるようになる。
Python 3.12 の type 文
Python 3.12 ではさらに簡潔な type 文が導入された。
type UserId = int type ScoreList = list[int] type UserScores = dict[str, ScoreList]
type 文は TypeAlias のインポートすら不要で、最もすっきりした書き方になる。3.12 以降を使えるプロジェクトならこちらが推奨される。
UserId: TypeAlias = int で型エイリアスを明示的に宣言可能に。
type UserId = int でインポート不要の専用構文が追加。最も簡潔。
複雑な型に名前を付ける
TypeAlias の真価は、ネストした複雑な型を整理するときに発揮される。
from typing import TypeAlias # エイリアスなし — 読みにくい def process(data: dict[str, list[tuple[int, int]]]) -> list[dict[str, float]]: ... # エイリアスあり — 意味が明確 CoordList: TypeAlias = list[tuple[int, int]] RegionData: TypeAlias = dict[str, CoordList] StatResult: TypeAlias = list[dict[str, float]] def process(data: RegionData) -> StatResult: ...
エイリアスに適切な名前を付ければ、型の構造だけでなくドメインの概念まで伝えられる。RegionData という名前からは「地域ごとのデータ」という意味が読み取れるが、dict[str, list[tuple[int, int]]] からは何も伝わらない。
コールバック関数の型にも使える
関数を引数に取るような場面でも型エイリアスは有効だ。
from typing import TypeAlias from collections.abc import Callable Handler: TypeAlias = Callable[[str, int], bool] def register(name: str, handler: Handler) -> None: ...
Callable[[str, int], bool] を毎回書くよりも、Handler という名前で扱えるほうが見通しがよい。引数が多い Callable ほどエイリアスの恩恵は大きくなる。
型エイリアスの注意点
型エイリアスはあくまで「別名」であり、新しい型を作るわけではない。
from typing import TypeAlias UserId: TypeAlias = int age: UserId = 25 # int なので当然通る
UserId と int は型チェッカ上まったく同じ扱いになるため、UserId を期待する場所に普通の int を渡してもエラーにならない。型レベルで区別したい場合は NewType を使う手があるが、それはまた別の話になる。
既存の型に読みやすい名前を付ける。型チェック上の区別はない。
既存の型を基にした「別の型」を定義する。型チェッカが区別してくれる。
型エイリアスは「同じ型を何度も書くのを避ける」「複雑な型に意味のある名前を付ける」ためのツールだ。プロジェクトの規模が大きくなるほど、整理された型エイリアスの存在がコードの可読性を大きく左右する。