組み込みのエラーオブジェクトのみを使用する
一段落説明
多くのコードフローの選択肢(EventEmitter、コールバック、Promises など)を持っているという JavaScript の寛容な性質が、開発者のエラー発生方法に大きな差をもたらしています - 文字列を使用する人もいれば、独自のカスタム型を定義する人もいます。Node.js の組み込みエラーオブジェクトを使用することは、コード内やサードパーティのライブラリ間において一貫性を保つことを助け、さらに スタックトレースのような重要な情報を保持します。通常、例外を発生させるときは、エラー名や関連する HTTP エラーコードといった追加のコンテキスト属性情報を付与することがベストプラクティスです。この一貫性保持やプラクティスを達成するために、エラーオブジェクトを追加プロパティで拡張することを考えますが、やりすぎには注意が必要です。一般的に、すべてのアプリケーションレベルのエラーに対して、AppError という形で一度だけ組み込みのエラーオブジェクトを拡張し、異なる種類のエラーを区別するために必要なデータを引数として渡すことをおすすめします。何回も(DbError、HttpError のようにそれぞれのケースに応じて)エラーオブジェクトを拡張する必要はありません。以下のコード例を参考にしてください。
コード例 – 正しい方法
// 同期または非同期に、典型的な関数からエラーを投げる
if(!productToAdd)
throw new Error('How can I add new product when no value provided?');
// EventEmitter からエラーを「投げる」
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
// Promise からエラーを「投げる」
const addProduct = async (productToAdd) => {
try {
const existingProduct = await DAL.getProduct(productToAdd.id);
if (existingProduct !== null) {
throw new Error('Product already exists!');
}
} catch (err) {
// ...
}
}
コード例 – アンチパターン
// 文字列を投げると、スタックトレース情報やその他の重要なデータプロパティを失います
if(!productToAdd)
throw ('How can I add new product when no value provided?');
コード例 – より優れた方法
Javascript
// Node のエラーから派生した、集中化されたエラーオブジェクト
function AppError(name, httpCode, description, isOperational) {
Error.call(this);
Error.captureStackTrace(this);
this.name = name;
//...他のプロパティがここで割り当てられます
};
AppError.prototype = Object.create(Error.prototype);
AppError.prototype.constructor = AppError;
module.exports.AppError = AppError;
// 例外を投げるクライアント
if(user == null)
throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, 'further explanation', true)
Typescript
// Node のエラーから派生した、集中化されたエラーオブジェクト
export class AppError extends Error {
public readonly name: string;
public readonly httpCode: HttpCode;
public readonly isOperational: boolean;
constructor(name: string, httpCode: HttpCode, description: string, isOperational: boolean) {
super(description);
Object.setPrototypeOf(this, new.target.prototype); // プロトタイプチェーンを復元する
this.name = name;
this.httpCode = httpCode;
this.isOperational = isOperational;
Error.captureStackTrace(this);
}
}
// 例外を投げるクライアント
if(user == null)
throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, 'further explanation', true)
TypeScript における Object.setPrototypeOf
の説明: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget