おそらく誰も使わない @babel/parser 7.14 新機能 TypeScript プラグイン の dts オプションについて
先日 Babel 7.14.0 がリリースされました。それに伴って Babel のパーサーである @babel/parser
も 7.14.0 がリリースされました。
この記事では、@babel/parser
7.14.0 の TypeScript プラグインに実装された新機能 dts オプションについて解説します。
自分で実装しておいてアレですが、このオプションを実用するひとはおそらくほとんどいないんじゃないかと思います。もしこのオプションが実用上役に立ったひとがいれば興味があるので連絡をください。
また、この記事の前半部分は @babel/parser
について知らないひと向けの解説になっています。@babel/parser
やプラグイン機能についてはすでに知っていて dts オプションについてのみ知りたいという場合は typescript プラグインの dts オプション のところまで飛ばしてください。
Babel のパーサーを直接使う
多くのウェブ開発者がなんらかの形で Babel を使ったことがあるでしょう。
おそらくそのほとんどがトランスパイラとしての利用だと思います。つまり、新しいプロポーザルを使いたい場合であったり、TypeScript や Flow のような AltJS を使いたい場合であったり、本番環境で古い JavaScript を実行させたい場合に、Babel を使ってウェブブラウザ(や Node.js)が実行できる形に変換するというユースケースです。
それが Babel の主目的です。
しかし、一部のソフトウェアでは Babel のパーサーのみを使っているケースがあります。Babel のパーサーは@babel/parser
パッケージとして単独で使用できるようになっています。
たとえば、Prettierでは JavaScript、Flow、TypeScript をパースするために @babel/parser
を使用しています。
@babel/parser
は次のようなAPIで使うことができます。
const { parse } = require("@babel/parser");
const ast = parse(`const foo = "foo"`);
@babel/parser
のプラグイン機能
@babel/parser
にはプラグイン機能が存在します。
プラグインを有効にすることで、本来であればパースできない構文をパースできるようになります。
const { parser } = require("@babel/parser");
const code = `const foo: string = "foo"`;
parse(code); // 構文エラー、型注釈の構文をパースできない
parse(code, { plugins: ["typescript"] }); // typescript プラグインを有効にしているため型注釈の構文をパースできる
現在実装されているプラグインは https://babeljs.io/docs/en/babel-parser#plugins で確認できます。
プラグインという呼ばれ方をしていますが、サードパーティがプラグインを実装することはできません。これは、@babel/parser
の方針として決まっており、今後もそのような対応をすることはないでしょう。
なので、実際はプラグインというよりはオプションのようなものだと思っていただければいいと思います。
プラグインのオプション
一部の@babel/parser
のプラグインにはオプションがあります。
たとえば、pipelineOperator
プラグインにはproposal
というオプションがあります。この記事でこれについて詳しく解説することはしませんが、簡単にいえばパイプライン演算子のプロポーザルには3種類あり、どの種類の提案を使うかをオプションで指定できます。
const { parse } = require("@babel/parser");
parse(`x |> await f`, { plugins: [["pipelineOperator", { proposal: "fshap" }]] });
typescript
プラグインの dts
オプション
さて本題です。
@babel/parser
7.14.0 では、TypeScript の構文に対応するための typescript
プラグインに boolean
をとる dts
という新しいオプションが生えました。
次のようにして使います。
const { parse } = require("@babel/parser");
parse(`const foo: string = "foo"`, { plugins: [["typescript", { dts: true }]] });
parse(`const foo: string = "foo"`, { plugins: [["typescript", { dts: false }]] });
このdts
オプションは、TypeScript の型定義ファイルを正しくパースするためのオプションです。つまり、*.d.ts
ファイルを正しくパースするためのオプションということです。
*.d.ts
ファイルと通常の TypeScript の構文は全く同じだと思われていることが多いですが、実際には微妙に違う箇所があり、dts
オプションを使うことでそれを切り替えることができます。
現在 Babel チームが知っている*.d.ts
と通常のTypeScriptの構文のちがいはひとつだけです。それは関数のパラメータの最後が Rest Parameters であるとき、末尾カンマが許容されるかどうかという点です。
通常の TypeScript では関数のパラメータの最後が Rest Parameters であるとき、末尾カンマをつけると構文エラーになります。しかし、*.d.ts
では構文エラーが発生しません。
// 構文エラーが発生する
function foo(...rest,): string {}
// 構文エラーは発生しない
function foo(...rest,): string;
https://github.com/microsoft/TypeScript/issues/23070 によるとこの挙動は意図されたものです。
もともと TypeScript では Rest Parameters の末尾にカンマをつけることは構文上問題ありませんでした。しかし、https://github.com/microsoft/TypeScript/pull/22262 によって禁止されるようになりました。しかし、かなり影響範囲が大きくなることが想定されたので *.d.ts
ファイルに対しては禁止しないようになっているのです。
まとめると、すくなくとも現時点では @babel/parser
の typescript
プラグインの dts
オプションは、Rest Parameters の末尾カンマを許容するかどうかを決めるオプションということになります(厳密には、その他にももともと Ambient Context のみで発生する構文エラーがいくつかあり、dts
オプション有効時にはそれらのエラーも発生するようになっています)。
詳細が気になるひとはこの機能を追加した PR https://github.com/babel/babel/pull/13113 を参照してください。
dts
オプションは役に立たないのか?
Q. なぜ A. *.d.ts
ファイルを@babel/parser
で直接パースしたいひとはいないから。
Discussion