Python typing.Optionalとは?Noneを許容する型ヒントの書き方と「X | None」・省略可能な引数との違い
typing.Optional[X]は「X型かNoneのどちらか」を表す型ヒントで、X | NoneやUnion[X, None]とまったく同じ意味です。Python 3.10以降ならfrom typing import Optionalを書かずにstr | Noneと書けます。名前に反して「省略できる引数」という意味ではない点に注意してください。
この記事では、Optionalの書き方、型チェッカー(mypy)がNoneのチェック漏れを見つける様子、X | Noneとの違いとPython 3.14での表示の変化、引数の既定値= Noneとの関係、TypedDictのNotRequiredとの違いを、実行結果つきで解説します。実行結果はPython 3.12.15とmypy 2.4.0で確認しました(2026年10月18日確認)。
Optionalの基本の書き方
from typing import Optional
def find(uid: int) -> Optional[str]:
return "佐藤" if uid == 1 else None
print(find(1))
print(find(2))
佐藤
None
-> Optional[str]は「戻り値はstrかNone」という意味です。「見つからなければNoneを返す」関数によく付けます。型ヒントなので、実行結果そのものは付けても付けなくても変わりません。効果が出るのは、mypyなどの型チェッカーやエディタで確認したときです。
公式ドキュメントでも、Optional[X]はX | None(またはUnion[X, None])と同じだと説明されています(typing公式ドキュメント)。
書き方の早見表
| 書き方 | 意味 | 使えるバージョン |
|---|---|---|
Optional[str] |
strかNone | 3.5以降(from typing import Optionalが必要) |
Union[str, None] |
strかNone(同じ意味) | 3.5以降(from typing import Unionが必要) |
str | None |
strかNone(同じ意味) | 3.10以降(importは不要) |
Optional[list[str]] |
文字列のリストかNone | 3.9以降(list[str]の書き方) |
Optional[int | str]/int | str | None |
intかstrかNone | 3.10以降 |
3.10以降だけを対象にするならstr | Noneが短く読みやすいので、新しく書くコードではこちらがよく使われます。3.9以前でも動かす必要があるコードではOptional[str]を使います。
Noneのチェック漏れを型チェッカーが見つける
Optionalを付けるいちばんの価値はここです。
# a2.py
from typing import Optional
def find(uid: int) -> Optional[str]:
return "佐藤" if uid == 1 else None
name = find(2)
print(name.upper())
このコードは実行すると落ちます。
AttributeError: 'NoneType' object has no attribute 'upper'
mypyにかけると、実行する前に同じ問題を教えてくれます。
$ mypy a2.py
a2.py:7: error: Item "None" of "str | None" has no attribute "upper" [union-attr]
Found 1 error in 1 file (checked 1 source file)
「str | NoneのうちNoneのほうにはupperがない」と、まさに落ちる理由を指摘しています。最近のmypyは、Optional[str]と書いてもstr | Noneと表示します。
is not None で絞り込めばエラーは消える
# a3.py
from typing import Optional
def find(uid: int) -> Optional[str]:
return "佐藤" if uid == 1 else None
name = find(2)
if name is not None:
print(name.upper())
$ mypy a3.py
Success: no issues found in 1 source file
if name is not None:の中では、型チェッカーが「nameはstr」と判断します。これを型の絞り込み(narrowing)と呼びます。if name is None: returnのように先に抜ける書き方や、assert name is not Noneでも絞り込まれます。
戻り値をそのまま別の関数に渡す場合も同じです。
def first_upper(names: list[str]) -> str | None:
if not names:
return None
return names[0].upper()
v = first_upper([])
print(len(v))
$ mypy a8.py
a8.py:7: error: Argument 1 to "len" has incompatible type "str | None"; expected "Sized" [arg-type]
実行するとTypeError: object of type 'NoneType' has no len()になる箇所を、事前に見つけています。
Optional[X]・Union[X, None]・X | None は同じもの
from typing import Optional, Union
print(Optional[int])
print(Union[int, None])
print(int | None)
print(Optional[int] == Union[int, None] == (int | None))
Python 3.12での実行結果:
typing.Optional[int]
typing.Optional[int]
int | None
True
3つとも等しいと判定されます。表示が違うのは、3.13までOptional・Unionと|が別の型で作られていたためです。
Python 3.14からは、Union[int, str]とint | strが同じ型で作られるようになり、repr()の表示もint | strにそろいました(What’s New In Python 3.14、2026年10月18日確認)。実行時に型ヒントを文字列で比べているコードは、表示の変化に注意してください。
Python 3.9で X | None を書くとエラー
同じコードをPython 3.9.6で動かすと、int | Noneの行で止まります。
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'
3.9以前もサポートするライブラリではOptional[int]を使うか、ファイルの先頭にfrom __future__ import annotationsを書いて注釈を評価させない方法を取ります(注釈の中だけで使う場合)。
Optionalは「省略可能な引数」ではない
いちばん多い誤解です。
from typing import Optional
def f(name: Optional[str]) -> None:
print(name)
f()
TypeError: f() missing 1 required positional argument: 'name'
Optionalを付けても、引数を省略できるようにはなりません。公式ドキュメントも、Optionalは「既定値を持つ省略可能な引数」とは別の概念だと注意しています。省略できるようにするのは既定値です。
def f(name: Optional[str] = None) -> None: ...
f() # OK(省略できる)
f("佐藤") # OK
f(None) # OK(Noneを渡せる)
| 書き方 | 省略できるか | Noneを渡せるか |
|---|---|---|
name: str |
できない | できない |
name: str = "名無し" |
できる | できない |
name: Optional[str] |
できない | できる |
name: Optional[str] = None |
できる | できる |
既定値がNoneなら、型にもNoneを入れる
def add_item(name: str, tags: list[str] = None) -> None:
pass
$ mypy a4.py
a4.py:1: error: Incompatible default for parameter "tags" (default has type "None", parameter has type "list[str]") [assignment]
a4.py:1: note: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True
以前のmypyは、既定値がNoneなら自動でOptional扱いにしていましたが、現在はエラーになります(mypy自身がnoteでそう説明しています)。既定値をNoneにするなら、型もOptional[list[str]]かlist[str] | Noneにします。
def add_item(name: str, tags: list[str] | None = None) -> None:
if tags is None:
tags = []
tags.append(name)
この「既定値はNoneにして中で空のリストを作る」形は、tags: list = []と書いたときに既定値のリストが全呼び出しで共有されてしまう問題を避ける定番の書き方でもあります。
TypedDictのNotRequiredとの違い
辞書の型を決めるTypedDictでは、「値がNoneでもよい」と「キー自体がなくてもよい」は別物です。
from typing import TypedDict, NotRequired, Optional
class User(TypedDict):
name: str
nickname: Optional[str] # キーは必須。値はNoneでもよい
age: NotRequired[int] # キーがなくてもよい
ok: User = {"name": "佐藤", "nickname": None}
ng: User = {"name": "鈴木"}
$ mypy a7.py
a7.py:9: error: Missing key "nickname" for TypedDict "User" [typeddict-item]
Optionalを付けたnicknameはキーを省略できず、NotRequiredを付けたageは省略できています。NotRequiredはPython 3.11からtypingに入っています。
戻り値をOptionalにするか、例外にするか
| 状況 | 向いている設計 |
|---|---|
| 見つからないのが普通にある(検索して0件など) | Optionalを返す |
| 見つからないのは異常(必須の設定がないなど) | 例外を出す |
| 呼び出し側がNoneのときの処理を毎回書ける | Optional |
| 呼び出し側では対処のしようがない | 例外 |
Optionalを返すと、呼び出し側すべてにNoneのチェックを求めることになります。安全ですが、多用するとif x is not None:だらけになるので、本当にNoneがありうる場所だけに使いましょう。Noneの判定方法は「Python None判定|Noneかどうかを正しく判定する方法」、複数の型を許す書き方は「Python Unionとは?複数の型を許容する型ヒントの書き方」で解説しています。
よくある質問
Q. Optional[str]とstr | Noneはどちらを使うべき?
意味は同じです。Python 3.10以降だけを対象にするなら、importが不要で短いstr | Noneが使いやすいです。3.9以前でも動かすコードではOptional[str]を使います。
Q. Optionalを付ければ引数を省略できますか?
いいえ。Optionalは「Noneを渡してよい」という意味で、省略できるようにするには= Noneのような既定値が必要です。
Q. Optional[str]とUnion[str, None]の違いは?
違いはありません。Optional[X]はUnion[X, None]を短く書くための書き方で、比較しても等しいと判定されます。
Q. 既定値をNoneにしたらOptionalは必要ですか?
必要です。現在のmypyは既定値がNoneでも自動でOptional扱いにせず、Incompatible default for parameterというエラーを出します。list[str] | None = Noneのように型にもNoneを入れます。
Q. OptionalとNotRequiredの違いは?
TypedDictで、Optionalは「キーは必須だが値はNoneでもよい」、NotRequiredは「キーそのものを省略してよい」という意味です。両方を満たしたいときはNotRequired[str | None]のように組み合わせます。
Optionalのまとめ
Optional[X]は「XかNone」。Union[X, None]・X | Noneと同じ- 3.10以降は
X | Noneと書ける。3.14からは表示もX | Noneにそろった - 型チェッカーがNoneのチェック漏れを実行前に見つける。
is not Noneで絞り込む - 「省略可能」という意味ではない。省略には既定値が必要
- 既定値が
Noneなら型にもNoneを入れる(暗黙のOptionalは現在のmypyでエラー) - TypedDictでキーを省略可能にするのは
NotRequired
型ヒントは、書いたコードを人に読んでもらうと効果がよくわかります。テックジムでは、自分のペースで課題を進めながら、トレーナーにコードを見てもらい質問できます。東京本校で毎週土曜に開いている無料Python講座(ゼロからはじめるPython入門講座)から試せます。



