TypeScript の ESTree は特に決まっていない
2022年の sosukesuzuki 1人アドベントカレンダー 13 日目です。
昨日出せなかったので今日急いで昨日の分を書いています。
ESTree とは
今日の記事を読む前に6 日目に書いた ESTree についての記事に目を通しておいてもらえるとわかりやすいと思います。
ESTree に含まれるのは ECMAScript だけ
ESTree を扱っているツールの一部は JavaScript だけではなく TypeScript を扱うことがあります。
たとえば typescript-eslint プロジェクトや Babel の TypeScript プラグインなどです。これらのツールは ESLint や Babel の上に乗っかっているため、ESTree にゆるく従った AST を扱います。
しかし ESTree には TypeScript にのみ含まれる構文の AST は定義されていません。
TypeScript にのみ含まれる構文というのは、たとえば
type Foo = { property1: stinrg };
のような型の宣言や、
function foo(value: string): void {}
の : string
や : void
の部分などです。
こういった構文に関しては ESTree ( https://github.com/estree/estree ) では定義されていません。ESTree はあくまで ECMAScript に含まれる構文に関する AST の仕様であるということです。
TypeScript の ESTree 風 AST
ですが typescript-eslint や Babel などのツールは TypeScript のコードを ESTree 風 AST に変換して扱いたいです。
ではどうしているのかというと、それぞれのツールが ESTree を拡張して TypeScript の ESTree 風 AST を定義して使っています。
しかしそれでは ESTree が解決したはずだった「各ツール間でゆるく AST の標準を決めておきたい」という課題が再び問題になります。
実はこの課題は、未だに解決されているとは言えない状態です。
実は、TypeScript の ESTree 風 AST には未だ仕様が存在せず、微妙に互換性を意識しながら typescript-eslint と Babel がそれぞれ独自に定義・実装をしているという歪な形になっています。
新しく TypeScript の構文が追加されたとき、最近では次のような流れで typescript-eslint と Babel が扱う AST が決定されます。
- TypeScript の新しい構文を含むベータ版がリリースされる
- Babel や typescript-eslint、Prettier のメンテナーたちがそれに気づく
- 気づいた人が AST をアラインしましょう!というような issue をどこかのリポジトリ(typescript-eslint か Babel)に開く
- そこで議論する
- 決定する
- 実装する
なので今でも仕様は存在せず中途半端な感じになっています。
とはいえ以前に比べるとより強く互換性を意識していて、typescript-eslint には Babel との互換性のためのテストがあるし(多くのテストケースが ignore されているが)、Babel の次のメジャーバージョンである 8.0 ではいくつかの構文の AST について互換性が改善される予定です。
ちなみに typescript-eslint と Babel の AST がズレていることによって最も影響を受けるソフトウェアの一つは Prettier です(両方の AST を一貫して扱えると楽だから)。なので 2 年くらい前に Prettier のメンテナーとして、typescript-eslint と Babel のメンテナーたちに対して「そろそろ TypeScript の AST の仕様を定めませんか?ESTree みたいにさ」というような提案をしましたが、話がまとまりきらずに終わりました。両方のメンテナーから難色を示されたような記憶があります。おそらく、大きな労力を割いてまで解決するような問題ではないのでしょう。
おまけ
TypeScript コンパイラー
TS AST Viewer とかで見るとわかりますが、TypeScript コンパイラー本家はもっと低レベルな AST を使っていて、ESTree とは全然違う感じです。