TypeScript で string 型の値に自動補完を効かせる2021年09月11日 12時12分

結論

type X = 'foo' | 'bar' | (string & {});

のように、文字列リテラル型の共用体型に | (string & {}) を付け足した型 X を定義します。X 型は任意の文字列を受け付けますが、IDE (Visual Studio Code など) で X 型の値を入力するときには 'foo''bar' が自動補完の候補として提示されます。

解説

単純に type X = 'foo' | 'bar' | string; と書いてしまうと、共用体型の各要素がまとめられて、X は単なる string 型になってしまいます。{} 型は nullundefined を除く任意の値を受け付けるので、string & {} 型は実質的に string 型と同一なのですが、TypeScript 4.4 の時点では同一扱いされず、共用体型の各要素がまとめられることもありません。

この手法を知った経緯

React の型定義の変更履歴を見たところ、input 要素の type 属性の型定義を追加するという変更が入っていました。そこには見慣れない表記が交じっており、コメントには「| (string & {}) ハック」と書かれています。

input 要素の type 属性 (HTMLInputElement インターフェイスの type プロパティ) には、形式的には任意の文字列を指定できますが、実際に有効なのは textcheckboxradio など特定の値のみです。type プロパティの値を指定するときに、自動補完を使ってミスなく高速に入力したいというのは自然な考えでしょう。

しかし、type プロパティの型を string 型から文字列リテラル型の共用体型に変えてしまうと、これまで指定できていた値が指定できなくなったり、将来の input 要素の拡張に即座に対応できなくなったりと、互換性が失われてしまいます。型の互換性を維持しつつ IDE の入力支援を引き出すために、| (string & {}) ハックが生み出されたのだと思います。