TypeScript で JavaScript をパースするときの型
この記事では TypeScript から既存のパーサーライブラリを使って JavaScript をパースするときにどのように型をつけるか、自分がやっている方法を紹介する。TypeScript を使って1から JavaScript のパーサーを書く話ではないので注意。
仕事で、TypeScript で JavaScript をパースして構文木をゴニョゴニョやるツールを書いていた。
こういうとき、まずどのパーサーを使うかを考える。
パースする対象が絶対に JavaScript であることがわかっている場合、自分は Acorn を使うことが多い(TypeScript や Flow をパースする場合@babel/parser
を使うこともあるし、TypeScript Compiler API の AST にもアクセスしたい場合は@typescript-eslint/typescript-estree
を使うことがある)。
Acorn はきちんとメンテナンスされており、@babel/parser
に比べてパーサーとして余分な機能がなくサイズが(比較的)小さいなどの特徴がある。なので、特にブラウザ上で動作させるツールの場合は Acorn を使うことが多い。
以前 Zenn のスクラップに ESTree っぽい AST を吐くパーサーをまとめたので、そちらも参照してほしい。
しかし、Acorn の型定義はあまり質が高くない。
ソースコードを実際に見てみるとわかるが、各ノードの型が用意されていない。これでは取得した AST を TypeScript から操作するときに不便である。
なので、自分は型定義だけ @types/estree
を使うことが多い。
@types/estree
のソースコードは以下にある。
acorn のパース結果を as
でキャストして ESTree の型を使っている。
import { parse } from "acorn";
import type { Node } from "estree";
const ast = (parser(code, { ecmaVersion: 2020 })) as Node;
もしかしたら実際の AST と型の間で差があるかもしれないが、Acorn は ESTree に準拠するとしており、@types/estree
は当然 ESTree に準拠するはずなので、バグってる場合直すことができるはずだ。
もしほかに良い方法がある場合は教えてほしい。
Discussion