一見 TypeScript に見える JavaScript コード
背景
Prettier 2.3 から代入周りのフォーマットの挙動が大幅に変更された。
それによっていくつかバグが発生し、2.3.1でその一部を修正した。
そしたら次のようなバグが報告された。
簡単に説明すると
次のような TypeScript のコードを Prettier でフォーマットすると
const [resolvedHexVersionId, setResolvedHexVersionId] =
useState<HexVersionId | undefined>();
次のようになってしまうというバグ報告である。
const [resolvedHexVersionId, setResolvedHexVersionId] = useState<
HexVersionId | undefined
>();
たしかに読みにくいと思う。
しかし、代入(や変数)の右辺に関数呼び出しがあり、その型引数が Union Types もしくは Intersection Types の場合はこんな感じにするという処理が Prettier 2.3.1 から存在し、それには合理性があると思っている。Union Types や Intersection Types が一定以上長い場合は2.3.1のようなフォーマットになっている方が整ってみえる(と思う)。
この記事の本題ではないので、気になるひとは該当の Issue や https://github.com/prettier/prettier/pull/10949 や https://github.com/prettier/prettier/issues/10846 を読んで雰囲気を把握してほしい。
そして今そのあたりをできる限りいい感じにする処理を書いている。
これは、Union Types や Intersection Types の中にnull
もしくはundefined
が含まれていた場合は特別なケースとして扱うというものだ。(Intersection Types はともかく)Union Types にnull
やundefined
を含むのはよくあるケースだと想定し、そこをカバーできれば多くのケースをカバーできるはずだという考えに基づいている。
本題
ここからが本題で、Prettier の話は全然関係ない。
上記の作業をしているとき、こんなコードを見つけた。
const [options, setOptions] =
useState<ParameterOptions | undefined>(initialOptions);
これは(多分)React の useState
を ParameterOptions | undefined
という型引数を渡して、initialOptions
という引数で呼び出し、その結果を options
とsetOptions
という変数で受け取るコードである。
これは実は JavaScript として妥当なコードでもある。
このコードを JavaScript として Prettier でフォーマットすると、次のようになる(Prettier Playground のリンク)
const [options, setOptions] =
(useState < ParameterOptions) | (undefined > initialOptions);
どういうことかというと、型引数の<
と>
が比較演算子とみなされ、Union Types の |
が JavaScript のビット論理和演算子として解釈されているのだ。
だからなんだという話だが、おもしろいと思ったので記事を書いた。
おわりに
おもしろかった。
Prettier の話について、なにか良い案がある人は PR か Issues を待っています。
Discussion