一般に「TypeScriptはJavaScriptの上位互換であって、任意のJavaScriptコードはTypeScriptとしても動く!」とか言われていますがいや、動かないコード割とたくさんないか?というお話。
確かに機能単位で見ると98%以上の部分がそのまま動きますが、残りの2%がコードのどこかしらに入っていることが多いので、コードベース全体で見るとそのまま動くことは稀な気がするんですが。
互換でない部分
JavaScriptコードがTypeScriptとしてそのまま動かない部分2つを挙げます。
オブジェクトのプロパティを後から追加する
例えば以下のJavaScript、
const obj = { a: 0, b: 1 };
obj.c = 2;
これはTypeScriptだとエラーになります。
tsconfig.json
でエラー扱いしないようなオプションがあれば良いのですが、私が調べた限りない。
上記のコードだと現実味がないですが、例えば
function createPerson(type) {
const obj = { type, firstName: '', lastName: '' };
if(type === 'student') {
obj.grade = '';
}
return obj;
}
みたいなコードを書いてる場所が割とたくさんあるので困る。
ES2015のクラス構文でのフィールド
ES2015からはJavaScript側でもクラス構文が導入されており、今では広く使われています。しかしES2015でのクラスがTypeScriptでそのまま動くことはほぼ無いと言っていいと思います。
なぜならTypeScriptではフィールドの明示的な宣言が必要だからです。例えば以下のJavaScript、
class AddNode extends Node {
constructor(lhs, rhs) {
super();
this.lhs = lhs;
this.rhs = rhs;
}
execute(ctx) {
return this.lhs.execute(ctx) + this.rhs.execute(ctx);
}
}
これはTypeScriptではエラーになります。TypeScriptでは以下のようにフィールドの宣言が必須です。
class AddNode extends Node {
lhs: Node;
rhs: Node;
constructor(lhs, rhs) {
super();
this.lhs = lhs;
this.rhs = rhs;
}
execute(ctx) {
return this.lhs.execute(ctx) + this.rhs.execute(ctx);
}
}
これも私の知る限り、tsconfig.json
でエラー扱いしないようにするオプションはないです。
クラスなんて今時普通に使うし、これで上位互換というのは嘘でしょうと思いましたね。
私がTypeScriptに求めていたこと
私がTypeScriptに最も求めていたことは端的に言えば関数の引数と戻り値の型のアノテーションをすること、そしてその関数を使う側がそれをドキュメントとして利用しまたエディタの補完機能によって快適にコーディングができるようにすることだったんですよね。ローカル変数やクラスのプライベートフィールドにまで正確な型付けをして完璧に型安全なコードにしようとは思っていなかった。型のない言語にはコードの記述量が少なく考えのままにサクサク書ける、仕様変更に柔軟に対応しやすい等それはそれで価値があると思っています。
TypeScriptはJavaScriptの上位互換で次第に移行しながら使えるという触れ込みだったので、じゃあドキュメント的に関数等の外向きの型のアノテーションのみに使うか、と思って導入したら互換性のない箇所が結構あったので、なんだよと思ったという話でした。