JavaScriptをはじめとする動的型付け言語では、プログラムの解釈時に型を判定しません
これは実行時まで変数や値には型が存在しないことを意味します
そのため型が自明なリテラルにおいても解析が一意となるような抽象化が図られています
例えば整数型はnumberという型で括られ、intなどの細かな区分はありません
型がないために、変数はあらゆる値を保持します
これは生成段階で型が静的に決まる型推論とは対照的な性質です
JavaScriptのconstも変数の値のみを固定し、型にまでは言及しません
function log(message){
const conment=message
}
とする時、このmessageがStringである保障はどこにもありません
例えば次のようなコードを考えてみましょう
const one=f=\u0026gt;x=\u0026gt;f(x)
const suc=a=\u0026gt;b=\u0026gt;c=\u0026gt;a((a(b))(c))
const test=suc(one)
これは引数の部分適用をテストするプログラムです
このコードはチャーチ数と呼ばれる数式を表現していますが、ミスが含まれています
しかし言語処理系はこれを判定しません
これを実行してもプログラムは正常に動作します
この時
test(x=\u0026gt;x+1)(0)
という引数を与えると、内部的に
x=\u0026gt;f(x)
という式が返されます
fはどこから出現した変数でしょうか
実はプログラムの解釈時に
a((a(b))(c))
という項が
one((one(x=\u0026gt;x+1))(0))
one((x=\u0026gt;(x=\u0026gt;x+1)(x))(0))
one(1)
(f=\u0026gt;x=\u0026gt;f(x))(1)
f=1
x=\u0026gt;f(x)
と展開されることで意図しない挙動が発生しています
fは1なので、ここでは整数が関数のように振る舞います
しかし整数は関数ではないため、この式にはどんな引数を渡しても次のエラーが返ります
TypeError: f is not a function
ここで重要なのは、これが実行時エラーである点です
このエラーは式を呼び出して初めて検知されます
式を解析した段階では内部の型エラーを感知しません
何故ならfには型がないからです
この式に本来期待される結果は以下です
test(x=\u0026gt;x+1)(0)
1
意図した結果を得るには、sucを次のように修正する必要があります
const suc=a=\u0026gt;b=\u0026gt;c=\u0026gt;b((a(b))(c))
これは式を適切に展開できなければ発見できないミスです
プログラムが引数に不適切な値を渡した時、動的型付けではそれを迅速に把握できません
引数にどんな値が渡されるかを予測することは難しく、事後的な型チェックでは厳密性を高度に担保できません
この解決のために型があります
例えばsucは次のように型付けできます
type Num=((f:Suc)=\u0026gt;(x:number)=\u0026gt;number)
type Suc=((x:number)=\u0026gt;number)
const one:Num=f=\u0026gt;x=\u0026gt;f(x)
const suc:((a:Num)=\u0026gt;(b:Suc)=\u0026gt;(c:number)=\u0026gt;number)=a=\u0026gt;b=\u0026gt;c=\u0026gt;b((a(b))(c))
test=suc(one)
sucに先のような間違った実装を与えると、処理系は解析時点でエラーを返します
const suc:((a:Num)=\u0026gt;(b:Suc)=\u0026gt;(c:number)=\u0026gt;number)=a=\u0026gt;b=\u0026gt;c=\u0026gt;a((a(b))(c))
Argument of type 'number' is not assignable to parameter of type 'Suc'.
型があることで、実装者は式の構成や展開の流れを正しく知ることができます
例えば型を持たないsucの実装では、ミスを把握するためにチャーチ数についての知見が必要でしたが、型があれば入出力の流れが予め可視化され、何を戻り値とすれば良いかが明確になります
これが静的型付けの優位性です
故にJavaScriptよりもTypeScriptが支持されています