2021-5-18
Result型とtry~catchどちらを利用するか
結論
HTTPエラーなど、明らかにどこかで処理すべき例外に関しては、Result型で対応する。
例えば、自分は現在通信をする際は以下のような関数を作成している。
type NetworkResult = Result<unknown, HttpError>
type HttpRequestOption<T> = {
url: string
method: 'get' | 'post' | 'put' | 'patch' | 'delete'
params?: URLSearchParams
data?: T
}
export const httpRequest = async <T>(
params: HttpRequestOption<T>
): Promise<NetworkResult> => {
try {
const response = await axios({
method: params.method,
url: params.url,
params: params.params,
data: params.data,
})
return new Success(response.data)
} catch (error) {
const isHttpError = error instanceof AxiosError
if (!isHttpError || error.response === undefined) {
// 通信エラー以外は想定外の例外なので例外として再スロー
throw error
}
if (error.response.status === 400) {
const errorBody = error.response.data as ApiErrorBody.BadRequest
return new Failure(
new HttpError({
type: 'bad_request',
data: errorBody,
})
)
}
return new Failure(new HttpError({ type: 'general' }))
}
}
理由
例えば、APIを叩いてユーザーを取得する fetchUser
という関数があるとする。これが通信失敗時に例外を投げる場合は
try {
await fetchUser(id)
} catch (error) {
if (error instanceOf HttpError) {
// 通信エラー時の処理
if (error.status === 404) {
// 中略
}
return
}
// 想定外の例外を握りつぶさないように再スローする必要がある
throw error
}
このコードには2点しんどい部分がある。
- 通信エラー時に投げられるエラーの型を知っておく必要がある
- 想定外の例外を握りつぶさないよう再スローしないといけない
これがResult型であれば。
const result = await fetchUser(id)
if (result.isFailure()) {
// 失敗時の処理
if (result.error.status === 404) {
// 中略
}
} else {
// 成功時の処理
}
このようになり、この時のエラーの型に関してはエディタが教えてくれるし、例外を握りつぶさないよう気をつける必要がない。
基本的にtry〜catchはどのようなエラーが飛んでくるかをドキュメントなり読まないとわからないのでしたくない。極力try〜catch無しでコードが書けるような作りにしたい。