TypedDictって何?一言でいうと「キー付き辞書に“型の設計図”を付けるもの」
TypedDict は、
「この辞書は、こういうキーを持っていて、それぞれの値はこういう型です」
という“設計図”を型ヒントとして書くための仕組みです。
普通の dict は何でも入ってしまいますが、TypedDict を使うと、
どんなキーが必須か
そのキーの値は何型か
省略可能なキーはどれか
を、型としてはっきり表現できます。
「クラスを作るほどではないけど、辞書の形はきっちり決めたい」
というときに、ちょうどいいポジションの道具です。
まずはシンプルな例でTypedDictの感覚をつかむ
「ユーザー情報の辞書」をTypedDictで表す
よくある「ユーザー情報の辞書」を考えます。
user = {
"id": 1,
"name": "Taro",
"email": "taro@example.com",
}
Pythonこれだけだと、
id は本当に int なのか?name は必ずあるのか?email を入れ忘れたらどうなるのか?
といったことが、コードからは分かりません。
ここで TypedDict を使います。
from typing import TypedDict
class UserDict(TypedDict):
id: int
name: str
email: str
Pythonこう書くと、「UserDict 型の辞書」は、
id キーを持ち、値は intname キーを持ち、値は stremail キーを持ち、値は str
という“形”を持つ、という意味になります。
実際の値はこうです。
user: UserDict = {
"id": 1,
"name": "Taro",
"email": "taro@example.com",
}
Pythonこの時点で、エディタや静的解析ツールは、
存在しないキーを使っていないか
値の型が間違っていないか
必須キーを入れ忘れていないか
をチェックできるようになります。
TypedDictがテスト・品質に効いてくるポイント
「辞書の形」を型として固定できる
普通の dict をそのまま使うと、
「キーのスペルミス」「キーの入れ忘れ」「型の取り違え」が
実行するまで分からないことが多いです。
TypedDict を使うと、
def send_mail(user: UserDict) -> None:
print(f"send to {user['email']}")
Pythonのように、「この関数は UserDict という形の辞書を受け取る」と宣言できます。
もしどこかで、
send_mail({"id": 1, "name": "Taro"}) # email を入れ忘れ
Pythonのようなコードを書いたら、
静的解析ツールが「email が足りない」と教えてくれます。
これは、「辞書の形のバグ」をテスト前に潰せる、ということです。
テストデータの「抜け」を減らせる
テストで辞書を組み立てるときも、TypedDict があると安心です。
def test_send_mail():
user: UserDict = {
"id": 1,
"name": "Taro",
"email": "taro@example.com",
}
send_mail(user)
Pythonもし email を書き忘れたら、
型チェックの時点で怒られます。
「テストデータのキーを入れ忘れていたせいで、実行時に KeyError」
みたいな事故を減らせます。
Optionalなキー(省略可能なキー)をどう表現するか
total=False で「一部のキーは省略可能」にする
現実のデータでは、「必須の項目」と「任意の項目」が混ざります。
例えば、「プロフィール画像 URL はあってもなくてもいい」ケース。
class UserProfile(TypedDict, total=False):
icon_url: str
Pythontotal=False を付けると、
その TypedDict のキーは「省略可能(オプショナル)」になります。
ただ、これだけだと「全部オプショナル」なので、
必須と任意を分けたいときはクラスを分けます。
class UserBase(TypedDict):
id: int
name: str
class UserWithIcon(UserBase, total=False):
icon_url: str
Pythonこうすると、
UserBase の id と name は必須UserWithIcon の icon_url は任意
という設計にできます。
Python 3.11以降の「Required / NotRequired」
Python 3.11 以降では、Required と NotRequired で、
キーごとに必須・任意を指定できます。
from typing import TypedDict, Required, NotRequired
class User(TypedDict):
id: Required[int]
name: Required[str]
icon_url: NotRequired[str]
Pythonこの書き方だと、
id と name は必須icon_url はあってもなくてもよい
ということが、より直感的に書けます。
dataclassとの違いを整理しておく
dataclassは「クラスのインスタンス」、TypedDictは「辞書」
dataclass でも、似たようなことができます。
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
email: str
PythonUser はクラスであり、User(id=1, name="Taro", email="...") のようにインスタンスを作ります。
一方 TypedDict は、あくまで「辞書の型」です。
class UserDict(TypedDict):
id: int
name: str
email: str
Python{"id": 1, "name": "Taro", "email": "..."} のような辞書に対して、
「この形をしている」と型を付けます。
どちらを使うかは用途次第ですが、
外部APIのレスポンス(JSON)
設定ファイルを読み込んだ結果
辞書で受け渡しするデータ構造
など、「もともと辞書で扱うデータ」に対しては、TypedDict の方が自然なことが多いです。
実践的な例:外部APIレスポンスをTypedDictで表す
ありがちな「dict[str, Any]地獄」を避ける
外部APIから JSON を受け取ると、
Python ではだいたいこうなります。
import requests
def fetch_user(user_id: int) -> dict:
resp = requests.get(f"https://example.com/users/{user_id}")
return resp.json()
Python戻り値の型が dict だけだと、
中身の構造がまったく分かりません。
ここで TypedDict を使います。
from typing import TypedDict
class UserResponse(TypedDict):
id: int
name: str
email: str
is_active: bool
def fetch_user(user_id: int) -> UserResponse:
resp = requests.get(f"https://example.com/users/{user_id}")
data = resp.json()
return data # 型的には UserResponse として扱う
Pythonこれで、
fetch_user の戻り値は、必ず id, name, email, is_active を持つ
それぞれの型も決まっている
という前提で、後続の処理を書けます。
user = fetch_user(1)
if user["is_active"]:
print(user["email"])
Pythonもしどこかで user["mail"] と書いてしまったら、
静的解析で「そんなキーはない」と教えてもらえます。
TypedDictを使うときの注意点
実行時には「ただのdict」であることを忘れない
TypedDict は、あくまで型ヒント用の仕組みです。
実行時には、UserDict のインスタンスは
普通の dict として振る舞います。
つまり、
user["unknown"] を実行時に叩けば、普通に KeyError になる
型ヒントを書いただけでは、実行時のバリデーションはされない
ということです。
実行時のチェックが必要なら、
pydantic などのバリデーションライブラリを使う
自前でチェックを書く
といった対策が別途必要です。
「なんでもTypedDict」はやりすぎ
便利だからといって、
すべての辞書に TypedDict を付ける必要はありません。
向いているのは、
外部とのインターフェース(APIレスポンス、リクエストボディ)
モジュール間で受け渡しする「構造が決まった辞書」
テストデータとして何度も出てくる辞書
など、「形が仕様の一部になっている辞書」です。
一時的な小さな辞書や、
その場限りのキーを持つ辞書にまで TypedDict を付けると、
逆にコードが重くなります。
初心者がTypedDictとどう付き合うといいか
まずは「外部APIレスポンス」か「設定」のどちらかから始める
最初の一歩としては、
外部APIのレスポンス
設定ファイルを読み込んだ結果
のどちらかを TypedDict で表現してみるのがオススメです。
レスポンスの JSON を見て、
どんなキーがあるか
どのキーが必須か
値の型は何か
を TypedDict に落とし込んでみる。
それを戻り値の型に付けて、
後続の処理を書いてみる。
これだけで、
「辞書の形を型で固定する」
「キーのスペルミスや入れ忘れを型で防ぐ」
という TypedDict の本質が体感できます。
「クラスにするか、TypedDictにするか」を意識して選ぶ
データ構造を作るときに、
クラス(+dataclass)にするか
辞書(+TypedDict)にするか
を意識して選ぶ癖をつけると、設計の解像度が上がります。
ロジックを持たせたいならクラス
ただのデータの塊で、辞書で扱うのが自然なら TypedDict
という分け方が、一つの目安になります。
まとめ(TypedDictは「辞書の形を型で固める」ための道具)
TypedDict を初心者目線で整理すると、こうなります。
TypedDict は、「この辞書はこのキーを持ち、それぞれの値はこの型」という“辞書の設計図”を型ヒントとして書く仕組みで、キーのスペルミスや入れ忘れ、型の取り違えを静的解析で検出できる。
必須キーと任意キーを分けたり(total=False や Required/NotRequired)、外部APIレスポンスや設定データなど「形が仕様の一部になっている辞書」を安全に扱えるようになる。
実行時にはただの dict なので、型ヒントだけではバリデーションはされないが、テストデータの抜けやインターフェースのズレを“テスト前に”見つけるための強力な道具になる。
「クラスにするほどではないが、辞書の形はきっちり決めたい」場面で使うと、設計と品質のバランスがとても良くなる。

