34
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

エラーハンドリングはResult型活用しようぜ

Posted at

Result型って何ですか

RustやSwiftだと馴染み深いResult型とは、その名の通り結果を表すものです。
TypeScriptだとその実装例として以下のものを私は使用しています。


export type Result<T = void, E = void> =
  | { ok: true; value: T }
  | { ok: false; error: E }

export const Ok = <T, E>(value: T): Result<T, E> => ({
  ok: true,
  value,
})

export const Err = <T, E>(error: E): Result<T, E> => ({ ok: false, error })

どう活用できるんですか?

エラーの種類に応じて処理を変えたい場合に相性良く活用できます。

例えば、Sign in処理を行う際に、ユーザーのリクエストが不正である場合と、それ以外のネットワーク起因の場合とで処理を使い分けたいときを想定してみます。
Result型を使うと下記のように書くことが出来ます。

// エラーを区別して出す関数
async function signin(payload: SigninPayload): Promise<Result<SigninResponse, "INVALID_REQUEST" | "UNKNOWN_ERROR">> {
	try {
		const res = await fetch("./login", { method: "POST", body: JSON.stringify(payload) })
		if (res.ok) {
			return Ok(res.json() as SigninResponse)
		}

		if (res.status === 400) {
			return Err("INVALID_REQUEST")
		}
		return Err("UNKNOWN_ERROR")
	} catch {
		return Err("UNKNOWN_ERROR")
	}
}

// SomeComponent.tsx
const SomeComponent = () => {
	const handleSubmit = () => {
		const result = await signin(payload)
		if (result.ok) return

		if (result.error === "INVALID_REQUEST") {
			console.log("パスワードとかが間違っているかも")
		}
		if (result.error === "UNKNOWN_ERROR") {
			console.log("レスポンスがJSONじゃなかったり、ネットワークエラーかも...")
		}
	}

	return <form onSubmit={handleSubmit}>...</form>
}

result.oktrueであれば、result.valueにアクセスしてその結果を受け取ることができ、そうでない場合はdata.errorにアクセスが可能でその値を元に処理を行えます。

Result型を使わないよくある方法として、try catchのcatchでのエラーハンドリングがある思います。

try {
    const result = await signin(payload)
} catch (err) {
    if (err instanceof CustomError) {
        console.log("独自のErrorクラス定義してハンドリング")
    }

    if (err instanceof Error) {
        console.log("CustomErrorより前にいると引っかかる")
    }
}

もちろんこれでも可能ですが、以下のデメリットが存在すると思います。

  • catchで掴む値はany型なのでちゃんと型安全に行うには、instanceof CustomError などを使う必要がある
  • Intellisenseのサポートを得られない。
  • 新しくスコープを作ってしまうことや、ネストが深くなる

一方Result型は

  • 型安全
  • Intellisenseのサポートを得られる
  • スコープもネストも深くならない
  • 何のエラーが返ってくるかという関心の分離が行える

ちなみにこういったライブラリも既にあるので、興味のある人は導入を検討してみても良いと思います。
(ただ個人的には上記の例程度のもので現状は十分だと感じています。)

よかったら導入を検討してみてください。

34
21
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?