なぜJavascrpitでは実行でき、Typescriptではできないのでしょうか。
この質問が一番重要な点ですが、Typescriptという言語が産み出された経緯を知ると理解しやすいかもしれません。
Javascriptは比較的柔軟な操作ができる一方で、その柔軟さがかえって多くの不具合の原因ともなってきました。
例えば、Object
をprototypeに保つ変数は toString()
というメソッドが必ず存在しますが、
const object = {}
console.log(obj.toString()) // '[object Object]'
console.log(obj.toSrting()) // Uncaught TypeError: obj.toSrting is not a function
のようにtypoしていたとしても、実際にコードを動かしてみるまで不具合に気づけません。
あるいは、
const num1 = 0
const num2 = "1"
console.log(num1 + num2) // '01' // 2を期待していた
といった暗黙の型変換による間違いもあります。
これらの特性はJavascriptが悪いという話ではなく、言語ごとの特性であり、柔軟さ・手軽さを好む人にとってはメリット、堅牢さを好む人にとってはデメリットともいえるものです。
普通であれば、「じゃあ堅牢さを好む人は別の言語を選択しよう!」ということになるわけですがJavascriptだけは特殊で、ブラウザ上では基本的にJavascriptぐらいしか動かない(今はWasmなどもありますが)ため別の言語に置き換えることができません。
そこで、
「Javascripの柔軟さの一部を捨ててでも、もっと堅牢で、例えば開発中に(実行前に)よくある間違いには気づけるような言語で開発できて、それでいてブラウザ上では動く言語はないものか」
として開発されたのがTypescriptです。
そのため、Typescriptは元来
「Javascriptでは確かに許されている操作なんだけど、間違いのもとだからあえてできなくしよう」
というルールを集めて実行前に検知する(そして最終的にはjavascriptに変換する)ための言語なのです。
一例として、Typescriptで
const num1: number = 0
const num2: string = "1"
console.log(num1 + num2)
と書くと、number型とstring型の加算は Javascriptでは許されているのにも関わらず トランスパイル時にエラーとなります。
(=それによって、実行前に間違えに気づけるかもしれない)
知っててわざとやってるのかもしれませんが、こういった演算は多くの場合ただのヒューマンエラーであることが多いからです。
また、
const obj: {a: number} = {a: 0}
obj.a = "hoge"
という操作も、obj.aにhogeを代入することは Javascriptでは許されているのにも関わらず トランスパイル時にエラーとなります。
「aにはnumberをいれる"つもり"だ」と宣言しているのにも関わらずstringをいれようとしているのは、知っててわざとやっているのかもしれませんが、多くの場合はヒューマンエラーだからです。
====
前置きが長くなってしまいましたが、ここで本題に戻ると、上記と同様に、Typescriptは
「Object型は toString
や valueOf
といったプロパティがあるよ(=しかないよ)」
と宣言しているのにも関わらず hi
を代入しようとする行為は、Javascriptでは確かに許されているが、あえて禁止されているのです
何故かというと、Typescriptはもともとそういった行為を禁止するために開発された言語だから、です
もし分かっててやりたい場合には、@_y_s さんがご紹介してくださっているリンクにもある通り、まずはObject型の型宣言を自分で上書きして、
「Object型には toString
や valueOf
や hi
があるよ」
と宣言しなおすことによって、Typescriptに「これは分かっててやってるんだよ」と伝えてあげる必要があるというわけです