Writer: tokuyasu 更新日:2025/10/23
普段はフロントエンドで TypeScript を書いていますが、Python での型定義に挑戦してみることにしました。
※Pythonでは「型ヒント(type hints)」と呼ばれますが、TypeScript との比較のしやすさから、この記事では「型定義」と表現しています。
TypeScript では型が当たり前ですが、Python でも型ヒントを使うことでコードの安全性や可読性を上げられます。
この記事では、TypeScript と比較しながら Python の型定義に取り組んだ体験をまとめます。
TypeScript では型が当たり前ですが、Python でも型ヒントを使うことでコードの安全性や可読性を上げられます。
この記事では、TypeScript と比較しながら Python の型定義に取り組んだ体験をまとめます。
dict の型付けってどうやるの?
TypeScript だと、例えば response の中に body があり、body だけ使う場合はこんな感じに書けますよね。
|
1 |
const response: { body: string } = { body: "hello" } |
Python でも同じように書こうと思って、まずは dict を使ってみました。
|
1 |
response: dict[str, str] = {"body": "hello"} |
でもこの書き方だとキーが文字列であることはわかりますが、「キーが何なのか」までは縛れません。
TypeScript ほど厳密にはならないんですね。
「body だけに絞りたいんだけど、、、」と思って調べることにしました。
TypedDict が TypeScript の型定義に近い
調べてみると、Python には TypedDict というものがありました。
これを使うと TypeScript の型定義に近い書き方ができます。
|
1 2 3 4 5 |
from typing import TypedDict class Event(TypedDict): body: str rules: list[int] |
こう書けば、Event 型のオブジェクトは必ず上記のキーを持つことが保証されます。
VSCode の補完も効くので、書き心地も TypeScript にだいぶ近いです。
さらにハマったポイント
さらにもうひとつ問題がありました。
from というキーを型ヒント内で使おうとしたら、エラーになってしまいました。
JavaScript / TypeScript では from は予約語ではないため普通にキーや変数名に使えますが、Python では予約語なので使用できません。
今回はレスポンスのキーを from から from_address に変更して対応しました。
もしレスポンスを変更できない場合は、受け取った後に別の名前へ変換することで対応できます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from typing import TypedDict, Any # 型定義 class Event(TypedDict): subject: str body: str from_address: str rules: list[int] # 外部から受け取ったレスポンス(from がキー) raw_event = { "subject": "Hello", "body": "Test message", "from": "user@example.com", "rules": [1, 2, 3] } # Python側でキー名を変更してTypedDictに変換 event: Event = { "subject": raw_event["subject"], "body": raw_event["body"], "from_address": raw_event["from"], # from を from_address に変換 "rules": raw_event["rules"], } # 利用例 print(f"Subject: {event['subject']}") print(f"Body: {event['body']}") print(f"From: {event['from_address']}") print(f"Rules: {event['rules']}") |
関数型スタイルで記述する TypedDict もあります。
関数型スタイルでは、クラス定義では使用できない予約語やハイフン入りなど、
通常の属性名としては使えないキーも型として定義できます。
しかし、クラス定義に比べて補完や可読性が劣り、記述が長くなると管理しにくいなどのデメリットがあるため、
実務では クラス定義スタイルがよく使われるようです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 関数型スタイル Event = TypedDict("Event", { "subject": str, "body": str, "from": str, "rules": list[int], }) # クラス定義スタイル class Event(TypedDict): subject: str body: str from_address: str rules: list[int] |
オプショナルなキーの書き方
TypedDict では、必須のキーだけでなく、オプショナルなキーも定義できます。
これは TypeScript で ? を使う書き方に近いイメージです。
|
1 2 3 4 5 6 7 8 |
from typing import TypedDict, NotRequired class Event(TypedDict): subject: str body: str from_address: str rules: list[int] metadata: NotRequired[dict] # このキーはオプショナル |
NotRequired[...] を使うと、そのキーが存在しなくても型チェックでエラーになりません。
外部 API のレスポンスでキーが存在しない場合も安全に扱えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
event1: Event = { "subject": "Hello", "body": "Test message", "from_address": "user@example.com", "rules": [1, 2, 3] } event2: Event = { "subject": "Hello", "body": "Test message", "from_address": "user@example.com", "rules": [1, 2, 3], "metadata": {"source": "api"} # オプショナルなので付けても付けなくてもOK } |
< 注意点 >
TypeScript ではオプショナルキーに直接アクセスすると、コンパイル時に警告やエラーが出ます。
しかし Python の TypedDict では、オプショナルキーに直接アクセスしても型チェック上はエラーにならず、
実行時にキーが存在しなければ KeyError が発生します。
|
1 |
metadata = event['metadata'] # 存在しない場合は KeyError |
TypedDict のオプショナルキーは型として「存在してもよい」と示すだけで、実行時の存在は保証されません。
そのため、オプショナルキーにアクセスする場合は安全策を意識することが重要です。
1. get() で取得する(存在しない場合は None が返る)
|
1 |
metadata = event.get('metadata') |
- ・キーがなくてもエラーにならず安全
- ・型チェックや補完は効く
- ・存在が不確定なオプショナルキーに便利
2. 存在チェックしてからアクセスする
|
1 |
metadata = event['metadata'] |
- ・キーが存在しない場合、型チェックでエラーになり、実行時も KeyError が出る
- ・存在が確実な場合は、コードがシンプルで、欠損を早期に発見できる
実務的な使い分け
★存在が確実なキー → event['key'] で直接アクセス
(理由: コードが簡潔で、欠損は早期に発覚)
(理由: コードが簡潔で、欠損は早期に発覚)
★存在が不確定なキー → get() で安全に取得
(理由: KeyError を避け、実行時エラーのリスクを減らせる)
(理由: KeyError を避け、実行時エラーのリスクを減らせる)
・TypeScript の event.metadata? のイメージに近いですが、Python では 実行時の存在確認を意識することが重要です。
まとめ
1. Python でも型定義は有効
TypeScript のように必須ではありませんが、補完や型チェックが効くので書いておくと安全性が上がります。
2. 使う部分だけでも型定義しておく
外部 API の返り値から body しか使わない場合でも、TypedDict で body: str を定義しておくと、
後からコードを読む人や自分以外の人が修正する際のミスを減らせます。
3. TypedDict の書き方には 2 種類ある
クラス定義スタイル:補完や可読性が強く、通常はこちらを基本に使用
関数型スタイル:予約語やハイフン入りキーなど、クラス定義で使えない場合に使用
4. 予約語に注意
Python では from などの予約語はキー名としてそのまま使えません。
外部 API のレスポンスを受け取ったあとに変換する、もしくは別名のキーで TypedDict を定義することで対応可能です。
5. オプショナルキーの扱い
TypedDict では型上オプショナルキーとして定義できますが、実行時に必ず存在するとは限りません。
存在が確実なキー → event['key'] で直接アクセス(コードが簡潔で、欠損は早期に発見可能)
存在が不確定なキー → get() で安全に取得(KeyError を避け、実行時エラーのリスクを減らせる)
Python では、TypeScript の event.metadata? のように
「存在チェックが型チェックで保証されるわけではない」ことに注意が必要です。
ここまで読み進めていただきありがとうございます。