先日この記事を読みました。
「例外を投げない」という選択肢をとる言語
Go や Scala では、例外を JavaScript のように throw して catch することが推奨されず、多値返却や Either でエラーも戻り値として返すアプローチがとられるという内容です。
TypeScript でも同じようなことがしたかったので fp-ts を使って作ってみました!!!🎉🎉
⛵ TrySafe
https://github.com/yarnaimo/trysafe
yarn add trysafe
か npm i -S trysafe
でインストールできます🙃
どっかで見たことあるような名前ですが気のせいです。
使い方
import { Try, TryAsync } from 'trysafe'
const result = Try(() => {
return document.querySelector('a')!.href // 要素が見つからない場合はエラーが throw される
})
// この場合 result の型は Either<Error, string>
if (result.isLeft()) {
// ここでは result.value の型は Error
console.error(result.value.message) // -> エラーメッセージ
return
}
// ここでは result.value の型は string
console.log(result.value) // -> href の値
Try
には引数のない関数 () => T
を渡します。
Try
の戻り値の型は fp-ts の Either<Error, T>
で、これは Left
型と Right
型の Union Type です。
渡された関数の中でエラーが throw された場合は Left
、成功した場合は Right
が返され、.isLeft()
と .isRight()
で判別することができます。
async 関数を渡したい場合は TryAsync
を使います。
const result = await TryAsync(async () => {
return await fetch('http://example.com')
})
普通の try-catch と比べていいところ
「エラーが起きる可能性」を明示できる (try-catch を忘れることがない)
例えばこんな関数があったとします。
async function getUsers() {
return fetch('/users')
}
この関数はエラーを throw する場合があるので上流で catch すべきですが、特にネストが深い場合などは忘れてしまう可能性もあります。
async function getUsers() {
return TryAsync(async () => fetch('/users'))
}
Try
で囲むと、エラーが発生した場合も外には throw されずに戻り値で返されるのでその心配はありません。
エラーが握りつぶされにくい
エラーを上流で処理するような設計にしやすいので、下流でエラーを握りつぶしてしまう可能性も減るのではないかと思います。
あとがき
今回の内容とは関係ないですが、fp-ts といえば実行時に型チェックをするための io-ts っていうライブラリもあります。
終わりだよ〜