TypeScriptで"ユニオン型あるいはなにかしらの型"を表現する方法について

型定義書いていて『Union型に当てはまる場合はそのどれかだが、どれにも当てはまらなかったら"この型"』みたいなものを書きたくなるときがある。

例えば

type UnionA = 'tarou' | 'jirou' | 'saburou'

というUnion型があって、このUnionA型に当てはまらなかったらstringみたいなことを表現したくなったとする。

逆に言うとUnionAの形式で補完が効いてほしいけど、補完以外のstringも受け付けたいみたいな欲求かもしれない。

const foo = '|' // ここで補完出すと 'tarou' | 'jirou' | 'saburou'が出て欲しいけど、別に'hogehoge~'みたいなstringでもOKみたいなわがまま型にするには…

人間的にはこう書けばいけそうな気がして書くがうまくいかない…

type UnionOrString = UnionA | string

const foo: UnionOrString = '|' // 補完が効かない!

そんな〜って気持ちになって破滅する。

LiteralUnion

たまたまSindre氏の型定義集のライブラリ眺めてたところ上記の問題を解決できる便利型が転がっていた。

github.com

export type LiteralUnion<
    LiteralType extends BaseType,
    BaseType extends Primitive
> = LiteralType | (BaseType & {_?: never});

https://github.com/sindresorhus/type-fest/blob/71613595b71473b2094b0803f21080056242c571/index.d.ts#L139-L169 より抜粋

これを使うと…

f:id:hitonatsu:20190326151311p:plain
やった〜

Playground · TypeScript

Issue

github.com

Issueはここみたいだけどコンパイラ的に解釈できないのでこういう型をいまのところは経由しないとダメみたい。

わりとこのわがままなUnion型をやりたい人は多そうな気がしているけど、ググラビリティが低すぎてみんなたどり着けていないのでは……という気がしている。

この記事もそういう意味ではたどり着けないのかもしれない。ユニオン型orなにかの型みたいなことを、もっとわかりやすく直接脳で解釈できるような言葉で書くの難しい気がする。