例外周りにあまり知見がなかったのでJavaScriptの標準のエラーから、TypeScriptでユーザー定義の例外を実装する方法までを調べました。
JavaScriptの標準のエラーオブジェクトについて
JavaScriptの標準のエラーオブジェクトは以下の表の通り7つあります。
エラーオブジェクト | 説明 |
---|---|
Error | ランタイムエラーが発生した時に投げられます。ユーザー定義の例外の基底オブジェクトとして使用することもできます。 |
EvalError | グローバルな eval() 関数に関連するエラーを示します。この例外はもう JavaScript から投げられませんが、EvalError オブジェクトは互換性のために残っています。 |
RangeError | 値が配列内に存在しない、または値が許容範囲にない場合のエラーを表します。 |
ReferenceError | 存在しない変数が参照された場合のエラーを表します。 |
SyntaxError | 構文的に不正なコードを解釈しようとした場合のエラーを表します。 |
TypeError | 値が期待される型でない場合のエラーを表します。 |
URIError | グローバル URI 処理関数が誤った使い方をされたことを示すエラーです。 |
こんなに定義されていたのですね。恥ずかしながらRangeErrorしか知りませんでした
TypeScriptでユーザー定義の例外を実装
先ほどの表ではAPIの通信エラーがあった場合、適切なエラーが見つからないのでErrorオブジェクトを使うことになります。
Errorオブジェクトは、ユーザー定義の例外の基底オブジェクトとして使用することもできます。
とのことなので、より適切なHttpRequestError
というエラーを作ってみましょう。
まずErrorオブジェクトを継承しApplicationError
をユーザー定義の基底オブジェクトを作成します。
ApplicationError.ts
export default class ApplicationError extends Error {
constructor(public message: string) {
super(message);
this.name = "ApplicationError";
}
}
// 最初はErrorインタフェースを実装していたのですが、
// 後ほど使うSentry.captureExceptionでErrorを継承する必要があった
// export default class ApplicationError implements Error {
ApplicationError
を継承したHttpRequestError
を作成。
HttpRequestError.ts
import ApplicationError from "./ApplicationError";
export default class HttpRequestError extends ApplicationError {
constructor(public message: string, public status: number) {
super(message);
this.name = "HttpRequestError";
this.message = `${status}: ${message}`
}
}
throw new HttpRequestError("管理者に連絡してください", 500)
標準エラーとユーザー定義エラーにより異なるアクションを実行
標準エラーとユーザー定義のエラーに基づいて、エラーハンドラー内で異なるアクションを実行します。(Vueのエラーハンドラーについてはコチラ)
errorHandler.ts
Vue.config.errorHandler = (error, vm, info) => {
// ApplicationErrorの場合はユーザーにアラートを表示し
if (error instanceof ApplicationError) {
alert(error.message);
if (error.stack) {
Sentry.captureException(error);
}
}
// ビルトインのエラーについてはユーザーに表示せず、Sentryなどで開発者に通知する
if (error instanceof Error) {
if (error.stack) {
Sentry.captureException(error);
}
}
};
さいごに
JavaScriptの例外のベストプラクティスを探して見つけることができませんでした。
自分で調べた結果このような形に落ち着いたのでしばらくはこの形を使用していく予定です。
間違いやアドバイスがあれば教えていただけると嬉しいです!