🧑💻
【TypeScript】Result型を定義してエラーハンドリングを楽にする
はじめに
TypeScriptでResult型を定義する方法はいくつかありますが、生成時や処理時の書き方が個人的に分かりにくいものだったので、初心者の方でも簡単に使えるResult型を作成したいと思います。
Result型とは?
Result型とは、何かしらの処理において、成功または失敗を表現できる型です。
これにより、try, catch
での苦悩を解放することができるようになります。
try, catchの問題点
TypeScriptでは、try, catch
でcatch
した例外は、any
として扱われます。
これでは、TypeScriptの型安全性の強みを全く活かすことができません。
try {
doSomething();
} catch(e) {
if (e instanceof HogeError) {}
else if (e instanceof FugaError) {}
.
.
.
// eの型がanyであるため、絞ろうにも絞れない
}
完成コード
今回作成したResult型がこちらとなります。
工夫して点としては、Result.success()
またはResult.failure()
で生成できるようにしたことや、結果処理を簡単にできるようにwhen()
を実装したことです。
class Result<T, E extends Error> {
private readonly value: T | E;
private constructor(value: T | E) {
this.value = value;
}
static success<T>(value: T): Result<T, Error> {
return new Result<T, Error>(value);
}
static failure<E extends Error>(error: E): Result<any, E> {
return new Result<any, E>(error);
}
when({
success,
failure,
}: {
success: (data: T) => any;
failure: (error: E) => any;
}) {
if (this.value instanceof Error) {
return failure(this.value);
} else {
return success(this.value);
}
}
}
使い方
使い方は簡単で、成功時のResultを生成したいときにはResult.success()
、失敗時のResultを生成したいときはResult.failure()
を使用するだけです。
Result.success(返したい値);
Result.failure(投げたいエラー);
また、返ってきたResult型に対して、成功時と失敗時の処理を実装する際には、when()
を使用するだけです。
const result = something();
result.when({
success(data) {
成功時の処理
},
failure(error) {
失敗時の処理
},
});
使用例
class HogeError extends Error {}
class FugaError extends Error {}
// 戻り値について
// 成功時:string
// 失敗時:HogeErrorまたはFugaError
function foo(value: string): Result<string, HogeError | FugaError> {
if (value === "foo") {
return Result.success("success");
} else {
// throwの代わりにResult.failure()をreturnする
return Result.failure(new HogeError());
}
}
// 例1
const result1 = foo('foo');
result1.when({
success(data) { // dataの型はstring
// 実行される
console.log(data); // foo
},
failure(error) { // errorの型はHogeErrorまたはFugaError
// 実行されない
if (error instanceof HogeError) {}
else if (error instance of FugaError) {}
},
});
// 例2
const result2 = foo('bar');
result2.when({
success(data) { // dataの型はstring
// 実行されない
},
failure(error) { // errorの型はHogeErrorまたはFugaError
// 実行される
if (error instanceof HogeError) {}
else if (error instance of FugaError) {}
},
});
Discussion
neverthrowのResult型でチャレンジしてみました。
デモコードです。
本記事でのwhen句にあたるのが、デモでのmatch句になります。
簡単ですが、以上です。