unreachableってなんですか
unreachable
とは下記で示される関数のことで、要するに到達して欲しくない & し得ない場所に防御的に使う関数です。
export const unreachable = (msg?: string) => {
throw new Error(msg ?? 'Something went wrong')
}
unreachableの活用方法
私はこのunreachable
の主に2つのユースケースで使用することが多いです。
パターン1: 三項演算子のチェック
type SomeUnion = 'foo' | 'bar'
const fooOrBar: SomeUnion = 'foo'
const hogeOrFuga =
fooOrBar === 'foo'
? 'hoge'
: fooOrBar === 'bar'
? 'fuga'
: unreachable('Unexpected value')
TypeScriptの型システムで守られていても、実際にSomeUnion
の値が入ってくるかは保証できないため、このようにunreachable
で検知できるようにしています。
パターン2: OptionalのUnwrap
unreachable
はnever
型を返す関数です。never
型を返す関数とはreturnされることの無い関数(throwで中断されたり、無限ループで永遠に終わらないなど)という意味です。
このnever
の特性を活かして下記のようにoptional値の強制Unwrapが可能です。
export const API_ENDPOINT = process.env.API_ENDPOINT ?? unreachable("API_ENDPOINT is not defined.)
少しだけ解説すると、process.env.API_ENDPOINT
の型は string | undefined
として扱われます。
もしprocess.env.API_ENDPOINT
がundefined
だった場合、 unreachable
が実行されプログラムは中断されるということが型情報から判別可能です。
そのためこの先プログラムが正しく動くという事は、unreachable
を通らないこと、すなわち process.env.API_ENDPOINT
は string
であるという型推論が可能となります。
特に環境変数などの値は相性がよいので、アプリのコードの随所で process.env
を参照せずに一箇所にまとめてUnwrapし管理すると良いと思います。
P.S.
特にパターン2のUnwrapのケースでは、invariant
を使うよりもunreachable
使ったほうが、スマートな気がするなぁ...なんて