一見 TypeScript に見える JavaScript コード

2 min read読了の目安(約2300字

背景

Prettier 2.3 から代入周りのフォーマットの挙動が大幅に変更された。
それによっていくつかバグが発生し、2.3.1でその一部を修正した。

そしたら次のようなバグが報告された。

https://github.com/prettier/prettier/issues/11037

簡単に説明すると

次のような 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/10949https://github.com/prettier/prettier/issues/10846 を読んで雰囲気を把握してほしい。

そして今そのあたりをできる限りいい感じにする処理を書いている。

https://github.com/prettier/prettier/pull/11042

これは、Union Types や Intersection Types の中にnullもしくはundefinedが含まれていた場合は特別なケースとして扱うというものだ。(Intersection Types はともかく)Union Types にnullundefinedを含むのはよくあるケースだと想定し、そこをカバーできれば多くのケースをカバーできるはずだという考えに基づいている。

本題

ここからが本題で、Prettier の話は全然関係ない。

上記の作業をしているとき、こんなコードを見つけた。

const [options, setOptions] =
  useState<ParameterOptions | undefined>(initialOptions);

これは(多分)React の useStateParameterOptions | undefined という型引数を渡して、initialOptionsという引数で呼び出し、その結果を optionssetOptionsという変数で受け取るコードである。

これは実は JavaScript として妥当なコードでもある。

このコードを JavaScript として Prettier でフォーマットすると、次のようになる(Prettier Playground のリンク)

const [options, setOptions] =
  (useState < ParameterOptions) | (undefined > initialOptions);

どういうことかというと、型引数の<>が比較演算子とみなされ、Union Types の | が JavaScript のビット論理和演算子として解釈されているのだ。

だからなんだという話だが、おもしろいと思ったので記事を書いた。

おわりに

おもしろかった。

Prettier の話について、なにか良い案がある人は PR か Issues を待っています。