静的型付けとは、型検査が実行の前に間違いを止める仕組み

回 6-4 道 6 言語と処理系

静的型付けは、プログラムを動かす前に式や変数の型を調べ、型の食い違いを止める仕組みです。この頁の小さな型検査器は、数と真偽を足す、while の条件が数、置いていない変数を読む、という間違いを 1 行も実行せずに止めます。動的型付けではその行に来るまで止まらず、通らない枝の間違いは最後まで見つかりません。

型検査が実行の前に止められる間違いと、型では止められない誤りを、小さな言語の木を調べて切り分けられるようになります。

動かす前に、間違いに気づけるか

気づける間違いがあります。プログラムを 1 行も動かさずに、式や変数の型 (値の種類) だけを調べて食い違いを止める仕組みを、静的型付けと呼びます。調べる作業を型検査と呼び、型の決まりの全体を型システムと呼びます。

たとえば 1 + (1 < 2) は、数の 1 と、比べた結果の真偽 (正しいか正しくないか) を足そうとしています。数と真偽は足せない、と決めておけば、この式は動かす前に「おかしい」と言えます。

この回は、6-3 で作った小さな言語「ちび電卓」の続きです。変数、print、if、while を持つ言語の木に、型 int (数) と bool (真偽) を付け、実行の前に木を 1 回たどる型検査を書きます。木の形から説明し直すので、6-3 を読んでいなくても分かります。

静的型付けは動かす前に型を調べ、動的型付けは動かしてその行で落ちることを比べた図

静的型付けはどう動くのか

静的型付けは、型を実行の前に決めて調べ、動的型付けは、実行しながら値の型を調べます。どちらの言語でも値には型があり、違うのは「いつ調べるか」です。

型が決まる時と、言語の例

比べる所静的型付け動的型付け
型を調べる時動かす前 (コンパイルや検査の時)動かしている途中、その行に来た時
型が付く所変数、引数、戻り値値
間違いが見つかる時動かす前に、通らない枝も含めてその行を通った時だけ
主な言語Java、C#、Go、Rust、TypeScriptPython、JavaScript、Ruby、PHP

静的型付け言語では、int と決めた変数に文字列を入れようとすると、動かす前に止まります。動的型付けの言語では、同じ名前に、数のあとで文字列を入れても動きます。型は名前ではなく、そのとき入っている値に付いているからです。

TypeScript は JavaScript に型を足した言語で、型を調べたあと、型の書き込みを消して JavaScript に直してから動かします。Python は動的型付けですが、型ヒント (型を書き添える荷札) を書くと、mypy などの道具が動かす前に調べます。

ちび電卓の木と、型の決まり

ちび電卓のプログラムは、Python のタプルで書いた木です。数は int そのまま、変数は名前の文字列、2 項の演算は (演算子, 左, 右) です。文は次の 4 つで、プログラムは文のリストです。

  • ("set", 名前, 式) 変数に値を置く
  • ("print", 式) 値を出す
  • ("if", 条件, [文…]) 条件が正しいときだけ中を動かす
  • ("while", 条件, [文…]) 条件が正しい間、中をくり返す

この木に、型の決まりを 4 つ足します。

  1. 数は int。+ - * / は int と int から int を作る (/ は整数の割り算)
  2. 比べる < と == は int と int から bool を作る。この回では、bool どうしの == は使わないことにする
  3. if と while の条件は bool でなければならない
  4. 変数は、最初に置いた値の型のまま。置く前に読んではいけない

型検査は木を 1 回たどる

型検査器は、木を根から葉までたどり、式ごとに型を計算します。数なら int、変数なら置いたときの型、演算なら左と右の型を先に求めてから決まりに当てます。

値は 1 つも計算しません。if や while の中も、条件に関わらず 1 回だけ調べるので、動かすと通らない枝の間違いも見つかります。ここが実行して確かめる方法との一番の違いです。

長所と短所

長所は次の通りです。

  • 間違いが、動かす前に、通らない枝も含めて見つかる
  • 型が説明書になり、エディターが候補を出したり、名前の付け替えを助けたりできる

短所は次の通りです。

  • 型を書く量が増える。型推論 (よくある問い 1) で減らせる
  • 決まりに当てはまらない書き方は、正しく動くコードでも止められる
  • 試作のように形がまだ決まらないうちは、型を合わせる手間が重く感じる

強い型と弱い型は別の物差し

静的か動的かは「いつ調べるか」の物差しで、強いか弱いかは「型の違う値を黙って変換するか」の物差しです。2 つは別なので、動的で強い言語も、静的で変換の多い言語もあります。

Python は動的型付けですが、文字列と数を足すと TypeError で止まります。JavaScript は "5" + 1 で 1 を文字列に変えてつなぎ、"51" にします。

ただし Python でも、bool は int の仲間 (下位の型) と決まっているので、1 + (1 < 2) は止まらずに 2 になります。ちび電卓の型検査は、ここをわざと Python より厳しくしています。

動かす 値に付く型と Python の足し算

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

int
str
2
True
TypeError

同じ名前 x に、数のあと文字列を入れても動き、型は値に付いていることが分かります。1 + (1 < 2) は 2 で、True は int の仲間です。文字列と数の足し算だけが、実行したその行で止まりました。

動かして確かめる

1 本目は、ちび電卓の型検査器です。6-2 の構文解析が作る木を、ここでは手で書きます。正しいプログラムとして 6-3 の「1 から 10 の合計」を検査し、そのあと間違いを入れた A から E を検査します。どれも 1 行も実行していません。

動かす ちび電卓の型検査

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

合計 通す
A 止める int + bool
B 止める while の条件が int
C 止める y が無い
D 止める x は int
E 止める int + bool

合計のプログラムは通りました。i < 11 は bool で、s + i と i + 1 は int です。

型検査が止めた間違いは次の 4 種類です。

  • A 数と真偽を足す。< の結果は bool なので、+ の右に置けない
  • B while の条件が数。条件は bool でなければならない
  • C 置いていない変数 y を読む
  • D int で置いた x に、bool を入れ直す

E は大事な例です。x は 5 なので x < 3 は正しくなく、if の中は動かすと決して通りません。それでも型検査は中まで調べ、x + (1 < 2) を止めました。

if や while の中で初めて置いた名前は、外では無い扱いにします (dict(env) で写しを渡す所)。中を通らなければ、本当に置かれないからです。

2 本目は比べです。同じ木を、型検査をせずにそのまま実行する 6-3 の形のインタプリタに渡します。こちらは、演算に来たときに初めて値の型を調べる、動的型付けのやり方です。

動かす 型検査なしで実行する

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

F
  1
  2
  止まる + に bool
E
  5
  最後まで動いた
G
  5
  最後まで動いた

F は 1 と 2 を出してから、3 行目に来たところで初めて止まりました。型検査なら、1 も 2 も出す前に止めています。前の行がファイルへの書き込みなら、途中まで済んだ状態が残ります。

E と G は「最後まで動いた」で終わりました。間違いのある行を通らなかったので、間違いがあることにすら気づきません。あとで条件が正しくなった日に、初めて止まります。

型検査で止められない誤りは何か

型検査が守るのは「値の種類が合っているか」だけで、値そのものが正しいかは見ません。型が合っていても起きる誤りの代表は、0 で割る、終わらない繰り返し、型の範囲を超える値の 3 つです。

次のコードは、短くした型検査を通したあと、同じ木を実行します。実行には、文を 1000 回動かしたら打ち切る見張りを付けています。型の間違いがあれば assert で止まります。

動かす 型は合っているのに壊れる

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

H 型検査は通す
  0 で割った
I 型検査は通す
  打ち切った

H の 10 / (x - 7) は、int を int で割るので型は正しく、割る数が 0 になるのは実行してからしか分かりません。I の while 0 < 1 も、条件は bool で型は正しいのに、終わりません。終わるかどうかは、型では分かりません。

3 つ目は値の範囲です。型が「整数」でも、入れ物の大きさには限りがあります。次のコードは、小数を整数に直し、16 ビットの符号付き整数 (-32768 から 32767) に詰めます。

動かす 型は int でも入らない

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

1234.5 1234 04d2
32767.0 32767 7fff
40000.0 40000 入らない

3 つとも int(v) の型は int で、型の上では同じです。それでも 40000 は 16 ビットに入りません。型検査は値が範囲に収まるかまでは見ません。次の節の事故は、この形で起きました。

静的型付けは仕事でどう確かめるのか

仕事では、型を調べる道具を CI (変更のたびに自動で走る検査) に入れ、型のエラーがあれば取り込まない形にします。型で止められない誤りは、試験で確かめます。

Python の型ヒントは、実行のときには調べません。公式文書も「Python の実行環境は関数や変数の型の注記を強制しない」と書いています。

動かす 型ヒントは実行では調べない

python実行可能
出力

▶ 実行ボタンを押すと結果がここに表示されます

実行すると、次の通りに出ます

42
abab
Hello World!
TypeError
n int
return int

twice("ab") は int のはずの所に文字列を渡しましたが、止まらずに abab を返しました。greeting(3) は、関数の中の足し算に来て初めて TypeError です。型ヒントは __annotations__ に残っているだけで、実行は読んでいません。

型ヒントは実行では強制されず、mypy などの検査の道具が動かす前に読むことを示した図

この荷札を読むのが mypy です。頁の中では動かないので、公式の入門の例を、幅に合わせて折って見せます。

mypy の入門の例 (折って表示)

greeting(3)
# Argument 1 to "greeting"
# has incompatible type
# "int"; expected "str"
greeting("World!")
# No error

確かめ方は次の通りです。

  1. Python なら、mypy などの型検査を CI で走らせ、型のエラーを 0 に保つ
  2. TypeScript なら tsc を CI で走らせる。公式の手引きによると、tsc は既定では型のエラーがあっても JavaScript を書き出すので、止めたいときは noEmitOnError を使う
  3. 0 で割る、終わらない繰り返し、値の範囲は型では止まらないので、境目の値 (0、上限、上限 + 1) を入れる試験を書く
TypeScriptとJavaScript

関わる実例

Ariane 5 の初飛行の失敗 (1996年6月4日)

1996年6月4日、欧州のロケット Ariane 5 の初飛行 (501 号) は、打ち上げの約 40 秒後に飛ぶ向きを外れ、壊れて爆発しました。欧州宇宙機関 (ESA) とフランス国立宇宙研究センター (CNES) が置いた調査委員会が、原因を報告書にまとめています。

報告書によると、慣性基準装置 (SRI、ロケットの向きと速さを測る装置) のソフトウェアで、64 ビットの浮動小数点数を 16 ビットの符号付き整数に変える処理が例外 (Operand Error) を起こしました。変えようとした値が、16 ビットの符号付き整数で表せるより大きかったためです。

値は水平の速さに関わる BH (Horizontal Bias) で、Ariane 5 の飛び方は Ariane 4 と違い、水平の速さがずっと大きくなりました。Ada で書かれたこの処理では、例外を起こしうる変数 7 つのうち 4 つだけを守り、BH を含む 3 つは余裕が大きいと考えて守っていませんでした。予備の装置も同じ理由で先に止まっていました。

崩れた前提は「型が合っていれば、値も収まる」です。変換は浮動小数点数から整数への、型の上では決められた操作でした。値が範囲を超えるかは、実際の飛び方の値でしか分からず、報告書は Ariane 5 の飛び方のデータを試験に使っていなかったことも挙げています。

出典は Ariane 501 の調査委員会報告の発表 (ESA) と 調査委員会の報告書 (ESA、PDF) です。

よくある問い

型推論があれば、型を書かなくても静的型付けですか

静的型付けです。型推論は、値や式から検査器が型を自分で決める仕組みで、型は動かす前に自動で付いています。ちび電卓の検査器も、("set", "x", 1) から x を int と決めており、型推論をしています。TypeScript の手引きも、多くの場合は型を書かなくても型を推論できると書いています。

静的型付けと強い型付けは同じですか

違います。静的か動的かは型を調べる時、強いか弱いかは型の違う値を黙って変換するかの違いです。Python は動的ですが、文字列と数の足し算では止まります。

TypeScript は静的型付けの言語ですか

TypeScript は JavaScript に静的な型検査を足した言語です。tsc が動かす前に型を調べ、型の書き込みを消した JavaScript を書き出します。動くのは型を消した JavaScript なので、外から届いたデータが書いた型と違っていても、実行中に型で止まることはありません。届いたデータは、受け取る所で中身を確かめます。

静的型付けは大きな開発に向いていますか

人が多く、コードが長く使われるほど、動かす前に見つかる間違いと、型が説明書になる良さが効きます。小さな試作では型を合わせる手間が重いこともあります。mypy の文書は、型ヒントを少しずつ足せる作りだと書いており、大事な所から型を書く進め方ができます。

確認クイズ

答えだと思う選択肢を押すと、その場で正誤と理由が開きます。4 問すべてに正しく答えると、この回は修了で、石が緑に変わります (この端末のブラウザに残ります)。間違えた問いは、あとから解き直せます。

1 問目

ちび電卓の型検査が、実行の前に止める間違いはどれですか。

2 問目

このコードを動かすと何が出ますか。

型ヒントと実行

def twice(n: int) -> int:
    return n * 2


print(twice("ab"))
print(twice(3))

3 問目

型検査をせずに実行する方法で、if の中の通らない枝に型の間違いがあると、どうなりますか。

4 問目

1996年の Ariane 5 の失敗の直接の原因として、調査委員会の報告書が挙げたのはどれですか。

説明課題

静的型付けの型検査が実行の前に止める間違いを 3 つ挙げ、型検査をせずに実行する方法との違いと、型検査では止められない誤りを 1 つ、3〜5 文で説明してください。「通らない枝」という言葉を使ってください。書いたら、下の模範解答を開いて比べてください。

模範解答

自分の説明を書いてから開いて、比べてください。

この回の先の一歩

TypeScript の tsc や Python の mypy が出す型のエラーを読めると、動かす前に直せる間違いと、試験や見張りでしか見つからない誤りを分けて、レビューや CI の守りを組み立てられます。

一次資料

どれも 2026年10月27日に開いて、題と中身を確かめました。

次へ

道 6 言語と処理系の回

  1. 6-1正規表現とオートマトン後戻りが増える式頁で読む
  2. 6-2字句解析と構文解析電卓の式を木にする頁で読む
  3. 6-3小さなインタプリタ変数と if を持つ言語頁で読む
  4. 6-4型と型検査型が間違いを先に止めるこの回

道 6 の頁へ戻るほかの道を見る