かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript catch したエラーをエラーの種類で別の処理にしたい

1つの catch 節でエラーをキャッチして、エラーの種類で処理を変えたい場合のメモ。

e.g.

function verify(token) {
  try {
    // 期限切れの場合 `TokenExpiredError` の例外が発生
    const verified = jwt.verify(token, process.env.TOKEN_SECRET);
    
    // revoke されてないか確認
    const validated = checkValidityToken(token);
    if (!validated) {
      return res.status(400).send('Token Expired');
    }
    
    return res.data = verified;
  } catch (err) {
    if (err instanceof TokenExpiredError) {
      return res.status(400).send('Token Expired');
    }
    // token が不正
    return res.status(400).send('Invalid Token');
  }
}

独自の checkValidityToken 関数で false が返された時に catch 節に処理を流して TokenExpiredError と同じ処理にしたい。

throw new Error

new Error(message) を throw すると catch 節の引数で取ることができる。

try {
  throw new Error('💩');
} catch (err) {
  console.log(err.name, err.message); // => Error 💩
}

new Error で作られたエラーオブジェクトの name プロパティは Error
MDN の Error 特定のエラーを処理する には Error クラスを extends で拡張して、error instanceof ErrorClassName でエラークラスで処理を分ける方法が載っていますが、error.name を利用すれば既存のライブラリで定義されているエラークラスに見せかけることができそうです。

Error.prototype.name

name ロパティは、エラーの種類の名称を表します。初期値は "Error" です。

var e = new Error('Malformed input'); // e.name is 'Error'
e.name = 'ParseError';
throw e;
// e.toString() would return 'ParseError: Malformed input'
cf. Error.prototype.name - JavaScript | MDN

name プロパティを利用して catch 節で error.name で処理を分岐させればOK。
元のコードを書き換えます

function verify(token) {
  try {
    // 期限切れの場合 `TokenExpiredError` の例外が発生
    const verified = jwt.verify(token, process.env.TOKEN_SECRET);
    
    // revoke されてないか確認
    const validate = checkValidityToken(token);
    if (!validate) {
      const validateError = new Error('Token Expired');
      validateError.name = 'TokenExpiredError';
      throw validateError;
    }
    
    return res.data = verified;
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(400).send('Token Expired');
    }
    // token が不正
    return res.status(400).send('Invalid Token');
  }
}

₍ ᐢ. ̫ .ᐢ ₎👌 A W E S O M E !

本当は TokenExpiredError を import してエラーオブジェクトを作成するのが正しいのだと思いますが、error.name を使っても同じ様な処理を実現することができました。ライブラリ内の実装読まなくて済むので特に問題が発生しないならこの方法の方が楽そうです。


[参考]

エラーキャッチ☆プリキュア