結論から言うと、TypeError は「型の使い方が合わない」とき、ValueError は「型は合っているが値の中身が合わない」ときに使います。

Pythonのエラーを読んでいると、TypeErrorValueError は何度も出てきます。
どちらも入力値に関係することが多いため、最初は似て見えるかもしれません。
この記事では、2つの違いを実務で判断しやすい形で整理します。

この記事でわかること

  • TypeErrorValueError の基本的な違い
  • 自分で例外を出すときの選び方
  • 入力チェックを読みやすく分ける考え方

完成コード

完成コードは次の通りです。

def normalize_discount_rate(rate: int | float) -> float:
    if not isinstance(rate, int | float):
        raise TypeError("割引率は数値で指定してください")

    if rate < 0 or rate > 100:
        raise ValueError("割引率は0から100の範囲で指定してください")

    return rate / 100


sample_rates = [10, 25.5, "30", -5, 120]

for rate in sample_rates:
    try:
        normalized_rate = normalize_discount_rate(rate)
    except TypeError as error:
        print(f"{rate!r}: 型を確認してください: {error}")
    except ValueError as error:
        print(f"{rate!r}: 値を確認してください: {error}")
    else:
        print(f"{rate!r}: {normalized_rate:.3f}")

コードのポイント

このコードでは、割引率を受け取り、計算に使いやすい小数へ変換しています。

  • 文字列のような数値ではない型なら TypeError を出しています
  • 数値ではあるものの範囲外なら ValueError を出しています
  • except TypeErrorexcept ValueError を分けて、確認する場所を変えています
  • 正しい値だけを 0.1 のような小数に変換しています

TypeErrorValueError を分けると、エラーを見たときに「型を直すのか」「値の中身を直すのか」を判断しやすくなります。

コードを順番に説明します

主要な処理を分けて説明します。

1. 型そのものが合っているか確認します

if not isinstance(rate, int | float):
    raise TypeError("割引率は数値で指定してください")

この部分では、rate が整数または小数かどうかを確認しています。
"30" のような文字列は、人間には数値に見えるかもしれませんが、Pythonでは文字列です。

関数が数値を前提にしているなら、文字列が渡された時点で型の使い方が合っていません。
このような場合は TypeError と考えると自然です。

2. 値の中身が条件に合っているか確認します

if rate < 0 or rate > 100:
    raise ValueError("割引率は0から100の範囲で指定してください")

この部分では、割引率が0から100の範囲に入っているかを確認しています。
-5120 は数値なので、型は合っています。
しかし、割引率として扱うには値の中身が不正です。

このように、型は合っているけれど業務ルールや関数の条件に合わない場合は ValueError が向いています。

3. 例外ごとに対応を分けます

except TypeError as error:
    print(f"{rate!r}: 型を確認してください: {error}")
except ValueError as error:
    print(f"{rate!r}: 値を確認してください: {error}")

TypeErrorValueError を分けて受け止めると、利用者に伝える内容を変えられます。
型が違うなら、入力元や変換処理を確認します。
値が範囲外なら、入力値そのものや業務ルールを確認します。

実務では、CSVやフォームから受け取った値は文字列になりやすいです。
そのため、どの段階で文字列を数値に変換するかも、エラーの読みやすさに関係します。

実務で使うときのポイント

自分で例外を選ぶときは、次の順番で考えると整理しやすくなります。

  • そもそも渡された型が違うなら TypeError
  • 型は合っているが値の範囲や形式が違うなら ValueError
  • 辞書のキーがないなら KeyError
  • リストの位置が範囲外なら IndexError

ただし、すべての関数で厳密な型チェックを書く必要はありません。
小さなスクリプトでは、Pythonが自然に出すエラーで十分な場合もあります。

一方で、共通関数や他の人が使う関数では、入り口で分かりやすい例外を出す価値があります。
エラーの原因が関数の中で深く進んでから出るより、最初に止まるほうが調査しやすいためです。

第4章の 4-4 で扱った raise と合わせて使うと、関数が受け付ける値の条件をコード上で明確にできます。
次の 4-6 では、実際のエラーメッセージをどの順番で読むかを整理します。

よくある勘違い・注意点

  • "10" は見た目が数値でも、Pythonでは文字列です
  • TypeError は型の問題、ValueError は値の中身の問題として考えると整理しやすいです
  • 入力値をすぐに変換する設計にすると、後続処理の型が安定します
  • 例外を分けすぎると読みにくくなることもあるため、実際に対応を変えたい単位で分けます

まとめ

  • TypeError は、型の使い方が合わないときに出ます
  • ValueError は、型は合っているが値の中身が条件に合わないときに出ます
  • 自分で raise するときも、この違いを意識すると読みやすくなります
  • 実務では、型を直すのか値を直すのかを切り分けられるとデバッグが速くなります