JavaScriptでカスタムエラークラスを作成する方法と実践ガイド

JavaScriptのエラーハンドリングは、プログラムの信頼性とメンテナンス性を向上させるために非常に重要です。標準のErrorクラスを使用することは一般的ですが、特定のエラーシナリオに対応するためにカスタムエラークラスを作成することで、エラーの識別と処理が容易になります。本記事では、カスタムエラークラスを作成する方法とその利点、さらに実際のプロジェクトでの応用例を詳しく解説します。これにより、JavaScriptのエラーハンドリングをより効率的かつ効果的に行えるようになるでしょう。

目次
  1. エラークラスの基礎
    1. Errorクラスの基本的な使い方
    2. Errorクラスのプロパティ
    3. 組み込みエラークラス
  2. カスタムエラークラスの作成
    1. 基本的なカスタムエラークラスの作成方法
    2. カスタムエラークラスにプロパティを追加する
    3. カスタムエラークラスの利点
  3. カスタムエラークラスの拡張
    1. カスタムエラークラスの階層化
    2. カスタムエラークラスのメソッド追加
    3. カスタムエラークラスのシリアライズ
  4. カスタムエラークラスの利用方法
    1. APIリクエストでのカスタムエラー
    2. フォームバリデーションでのカスタムエラー
    3. カスタムエラーのロギング
  5. エラー処理のベストプラクティス
    1. 特定のエラーをキャッチする
    2. 意味のあるエラーメッセージを提供する
    3. エラー情報のロギング
    4. 再スローの使用
    5. カスタムエラークラスの統一
  6. カスタムエラークラスのデバッグ
    1. エラーメッセージとスタックトレースの活用
    2. ブラウザのデベロッパーツール
    3. Node.jsのデバッグ方法
    4. エラーロギングサービスの利用
    5. エラーハンドリングのテスト
  7. 例外の再スローとカスタムエラー
    1. 例外の再スローの基本
    2. エラー情報の保持
    3. カスタムエラークラスのチェーン
  8. カスタムエラークラスのユニットテスト
    1. ユニットテストの基本
    2. 詳細なプロパティのテスト
    3. モックを使用したテスト
  9. カスタムエラークラスの実践例
    1. データベース操作におけるカスタムエラー
    2. APIリクエストのエラーハンドリング
    3. ユーザー入力のバリデーション
    4. ログイン認証のエラーハンドリング
  10. まとめ

エラークラスの基礎

JavaScriptには、エラーを表現するための組み込みクラスであるErrorクラスがあります。Errorクラスは、プログラム内で発生したエラーを表現し、エラーメッセージやスタックトレースを提供するために使用されます。

Errorクラスの基本的な使い方

Errorクラスを使用することで、エラーを発生させ、そのエラー情報を捕捉することができます。基本的な使い方は以下の通りです。

try {
    throw new Error("Something went wrong!");
} catch (error) {
    console.error(error.message); // "Something went wrong!"
    console.error(error.name);    // "Error"
    console.error(error.stack);   // スタックトレース
}

Errorクラスのプロパティ

Errorクラスには主に以下のプロパティがあります。

  • message: エラーメッセージを格納します。
  • name: エラーの名前を格納します。デフォルトでは”Error”です。
  • stack: エラー発生時のスタックトレースを格納します。

組み込みエラークラス

JavaScriptには、特定のエラータイプを表現するためのいくつかの組み込みエラークラスも存在します。代表的なものには以下があります。

  • TypeError: 型が不正な場合に使用されます。
  • ReferenceError: 存在しない変数を参照しようとした場合に使用されます。
  • SyntaxError: 構文が不正な場合に使用されます。
  • RangeError: 範囲外の値を使用した場合に使用されます。

これらのエラークラスは、Errorクラスを継承しており、特定のエラーシナリオに対してより適切なエラー情報を提供します。次のセクションでは、これらの基礎を踏まえて、カスタムエラークラスの作成方法について詳しく説明します。

カスタムエラークラスの作成

カスタムエラークラスを作成することで、特定のエラーシナリオに対してより詳細な情報を提供し、エラー処理をより柔軟に行うことができます。JavaScriptでは、Errorクラスを継承することでカスタムエラークラスを作成することが可能です。

基本的なカスタムエラークラスの作成方法

以下に、基本的なカスタムエラークラスの作成方法を示します。この例では、MyCustomErrorという名前のカスタムエラークラスを作成します。

class MyCustomError extends Error {
    constructor(message) {
        super(message); // Errorクラスのコンストラクタを呼び出します
        this.name = this.constructor.name; // エラーの名前をクラス名に設定します
        Error.captureStackTrace(this, this.constructor); // スタックトレースをキャプチャします
    }
}

// カスタムエラーをスローしてキャッチする例
try {
    throw new MyCustomError("This is a custom error!");
} catch (error) {
    console.error(error.name);    // "MyCustomError"
    console.error(error.message); // "This is a custom error!"
    console.error(error.stack);   // スタックトレース
}

カスタムエラークラスにプロパティを追加する

カスタムエラークラスに独自のプロパティを追加することで、エラーに関するより多くの情報を提供できます。以下の例では、statusCodeというプロパティを追加しています。

class HttpError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.name = this.constructor.name;
        this.statusCode = statusCode;
        Error.captureStackTrace(this, this.constructor);
    }
}

// カスタムエラーをスローしてキャッチする例
try {
    throw new HttpError("Not Found", 404);
} catch (error) {
    console.error(error.name);       // "HttpError"
    console.error(error.message);    // "Not Found"
    console.error(error.statusCode); // 404
    console.error(error.stack);      // スタックトレース
}

カスタムエラークラスの利点

カスタムエラークラスを使用する主な利点は以下の通りです。

  • 特定のエラーシナリオに対応:特定のエラーを明確に区別し、対応する処理を行いやすくなります。
  • エラー情報の拡充:エラーに関する詳細な情報を提供し、デバッグやロギングが容易になります。
  • コードの可読性向上:エラーハンドリングのロジックが明確になり、コードの可読性が向上します。

次のセクションでは、カスタムエラークラスをさらに拡張し、実際のプロジェクトでの応用例について詳しく説明します。

カスタムエラークラスの拡張

カスタムエラークラスは、特定のニーズに合わせてさらに拡張することができます。ここでは、実際のプロジェクトで役立ついくつかの拡張方法と応用例を紹介します。

カスタムエラークラスの階層化

複数のカスタムエラークラスを階層化することで、エラーの種類をさらに細分化できます。例えば、HTTPエラーに関するカスタムエラークラスを階層化することが可能です。

class HttpError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.name = this.constructor.name;
        this.statusCode = statusCode;
        Error.captureStackTrace(this, this.constructor);
    }
}

class NotFoundError extends HttpError {
    constructor(message = "Not Found") {
        super(message, 404);
    }
}

class UnauthorizedError extends HttpError {
    constructor(message = "Unauthorized") {
        super(message, 401);
    }
}

// カスタムエラーをスローしてキャッチする例
try {
    throw new NotFoundError();
} catch (error) {
    console.error(error.name);       // "NotFoundError"
    console.error(error.message);    // "Not Found"
    console.error(error.statusCode); // 404
    console.error(error.stack);      // スタックトレース
}

カスタムエラークラスのメソッド追加

カスタムエラークラスにメソッドを追加することで、エラーに関する追加の操作や情報提供が可能になります。

class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = this.constructor.name;
        this.field = field;
        Error.captureStackTrace(this, this.constructor);
    }

    logError() {
        console.error(`${this.name}: ${this.message} (Field: ${this.field})`);
    }
}

// カスタムエラーをスローしてキャッチする例
try {
    throw new ValidationError("Invalid input", "username");
} catch (error) {
    error.logError(); // "ValidationError: Invalid input (Field: username)"
}

カスタムエラークラスのシリアライズ

エラーオブジェクトをシリアライズしてログやネットワーク経由で送信することができます。これは、分散システムやログ管理システムで役立ちます。

class ApiError extends Error {
    constructor(message, code) {
        super(message);
        this.name = this.constructor.name;
        this.code = code;
        Error.captureStackTrace(this, this.constructor);
    }

    toJSON() {
        return {
            name: this.name,
            message: this.message,
            code: this.code,
            stack: this.stack,
        };
    }
}

// カスタムエラーをスローしてキャッチする例
try {
    throw new ApiError("Service unavailable", "SERVICE_UNAVAILABLE");
} catch (error) {
    console.error(JSON.stringify(error)); // シリアライズされたエラーオブジェクトを出力
}

これらの拡張方法を活用することで、カスタムエラークラスの柔軟性と有用性がさらに高まります。次のセクションでは、カスタムエラークラスを実際のコードでどのように利用するかについて詳しく説明します。

カスタムエラークラスの利用方法

カスタムエラークラスを実際のコードで利用する方法について詳しく説明します。ここでは、エラーハンドリングの具体的な例を通じて、カスタムエラークラスの効果的な活用方法を示します。

APIリクエストでのカスタムエラー

以下の例では、HTTPリクエスト中に発生する可能性のあるエラーを処理するために、カスタムエラークラスを使用しています。

class NetworkError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.name = this.constructor.name;
        this.statusCode = statusCode;
        Error.captureStackTrace(this, this.constructor);
    }
}

async function fetchData(url) {
    try {
        let response = await fetch(url);
        if (!response.ok) {
            throw new NetworkError("Failed to fetch data", response.status);
        }
        let data = await response.json();
        return data;
    } catch (error) {
        if (error instanceof NetworkError) {
            console.error(`Network error: ${error.message} (Status: ${error.statusCode})`);
        } else {
            console.error(`Unexpected error: ${error.message}`);
        }
        throw error;
    }
}

// 使用例
fetchData("https://api.example.com/data")
    .then(data => console.log(data))
    .catch(error => console.error("Error occurred:", error));

フォームバリデーションでのカスタムエラー

フォーム入力の検証において、特定のフィールドに対するバリデーションエラーをカスタムエラークラスで処理します。

class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = this.constructor.name;
        this.field = field;
        Error.captureStackTrace(this, this.constructor);
    }
}

function validateForm(formData) {
    if (!formData.username) {
        throw new ValidationError("Username is required", "username");
    }
    if (formData.password.length < 6) {
        throw new ValidationError("Password must be at least 6 characters long", "password");
    }
    return true;
}

// 使用例
try {
    let formData = { username: "", password: "12345" };
    validateForm(formData);
} catch (error) {
    if (error instanceof ValidationError) {
        console.error(`Validation error on ${error.field}: ${error.message}`);
    } else {
        console.error(`Unexpected error: ${error.message}`);
    }
}

カスタムエラーのロギング

エラー発生時にカスタムエラークラスを使用して詳細なログを残すことで、後で問題の原因を特定しやすくなります。

class DatabaseError extends Error {
    constructor(message, query) {
        super(message);
        this.name = this.constructor.name;
        this.query = query;
        Error.captureStackTrace(this, this.constructor);
    }
}

function executeQuery(query) {
    try {
        // 疑似データベースクエリ実行
        throw new DatabaseError("Failed to execute query", query);
    } catch (error) {
        if (error instanceof DatabaseError) {
            console.error(`Database error: ${error.message} (Query: ${error.query})`);
        } else {
            console.error(`Unexpected error: ${error.message}`);
        }
        throw error;
    }
}

// 使用例
try {
    executeQuery("SELECT * FROM users");
} catch (error) {
    console.error("Error occurred:", error);
}

これらの例を通じて、カスタムエラークラスを用いたエラーハンドリングの具体的な方法を理解できるでしょう。次のセクションでは、エラー処理のベストプラクティスについて詳しく解説します。

エラー処理のベストプラクティス

カスタムエラークラスを活用したエラーハンドリングは、コードの信頼性と可読性を向上させます。ここでは、エラー処理のベストプラクティスについて詳しく解説します。

特定のエラーをキャッチする

エラーハンドリングでは、特定のエラータイプを識別して適切に処理することが重要です。カスタムエラークラスを使用することで、エラータイプを明確に分けることができます。

try {
    // エラーが発生する可能性のあるコード
} catch (error) {
    if (error instanceof SpecificError) {
        // SpecificErrorの処理
    } else {
        // その他のエラーの処理
    }
}

意味のあるエラーメッセージを提供する

エラーメッセージは、発生した問題を迅速に理解するための重要な手掛かりです。エラーメッセージは具体的で意味のあるものにするべきです。

class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = this.constructor.name;
        this.field = field;
        Error.captureStackTrace(this, this.constructor);
    }
}

throw new ValidationError("Invalid input", "username");

エラー情報のロギング

エラーが発生した際に、詳細な情報をログに残すことは、後で問題をトラブルシュートするのに役立ちます。

class DatabaseError extends Error {
    constructor(message, query) {
        super(message);
        this.name = this.constructor.name;
        this.query = query;
        Error.captureStackTrace(this, this.constructor);
    }
}

try {
    // データベースクエリの実行
} catch (error) {
    if (error instanceof DatabaseError) {
        console.error(`Database error: ${error.message} (Query: ${error.query})`);
    } else {
        console.error(`Unexpected error: ${error.message}`);
    }
    throw error; // 必要に応じて再スロー
}

再スローの使用

エラーを再スローすることで、呼び出し元のコンテキストでもエラーを処理することができます。

try {
    try {
        // エラーが発生する可能性のあるコード
    } catch (error) {
        // ローカルでのエラー処理
        throw error; // 再スロー
    }
} catch (error) {
    // 呼び出し元でのエラー処理
}

カスタムエラークラスの統一

プロジェクト全体で一貫したエラー処理を行うために、カスタムエラークラスの設計と使用を統一することが重要です。これは、チーム全体でのコードの理解とメンテナンスを容易にします。

class AppError extends Error {
    constructor(message, code) {
        super(message);
        this.name = this.constructor.name;
        this.code = code;
        Error.captureStackTrace(this, this.constructor);
    }
}

class NotFoundError extends AppError {
    constructor(message = "Resource not found") {
        super(message, 404);
    }
}

class ValidationError extends AppError {
    constructor(message, field) {
        super(message, 400);
        this.field = field;
    }
}

これらのベストプラクティスを遵守することで、エラー処理が一貫性を持ち、コードの信頼性が向上します。次のセクションでは、カスタムエラークラスをデバッグする方法について詳しく説明します。

カスタムエラークラスのデバッグ

カスタムエラークラスをデバッグすることは、エラーの原因を特定し、修正するために不可欠です。ここでは、カスタムエラークラスのデバッグ方法とそのためのツールや技術について解説します。

エラーメッセージとスタックトレースの活用

カスタムエラークラスのデバッグには、エラーメッセージとスタックトレースが非常に重要です。エラーを発生させる際に、これらの情報を適切に設定し、キャッチ時にログ出力することでデバッグが容易になります。

class CustomError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
        Error.captureStackTrace(this, this.constructor);
    }
}

try {
    throw new CustomError("Something went wrong");
} catch (error) {
    console.error(error.message); // "Something went wrong"
    console.error(error.stack);   // スタックトレース
}

ブラウザのデベロッパーツール

ブラウザのデベロッパーツールは、JavaScriptのデバッグに非常に便利です。以下の機能を活用しましょう。

  • コンソール: console.errorconsole.logを使ってエラー情報を出力します。
  • ブレークポイント: エラーが発生する行にブレークポイントを設定し、実行中の状態を確認します。
  • コールスタック: コールスタックを確認し、どの関数呼び出しがエラーを引き起こしたかを特定します。

Node.jsのデバッグ方法

Node.jsを使用している場合、以下のデバッグ方法があります。

  • consoleオブジェクト: console.errorconsole.logでエラー情報を出力します。
  • デバッガー: node --inspectnode --inspect-brkを使用して、Chromeデベロッパーツールでデバッグできます。
  • 外部ツール: Visual Studio CodeなどのIDEには、統合デバッガーがあり、ブレークポイントの設定やステップ実行が可能です。

エラーロギングサービスの利用

SentryやLogRocketなどのエラーロギングサービスを利用することで、エラー情報を集中管理し、分析することができます。これにより、エラーの頻度や影響を把握しやすくなります。

import * as Sentry from "@sentry/browser";

Sentry.init({ dsn: "YOUR_SENTRY_DSN" });

class CustomError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
        Error.captureStackTrace(this, this.constructor);
    }
}

try {
    throw new CustomError("Something went wrong");
} catch (error) {
    Sentry.captureException(error);
}

エラーハンドリングのテスト

エラーハンドリングのテストは、カスタムエラークラスのデバッグに役立ちます。ユニットテストフレームワークを使用して、エラーが正しくスローされ、処理されることを確認します。

const assert = require("assert");

class CustomError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
        Error.captureStackTrace(this, this.constructor);
    }
}

try {
    throw new CustomError("Test error");
} catch (error) {
    assert.strictEqual(error.message, "Test error");
    assert.strictEqual(error.name, "CustomError");
}

これらの方法を活用することで、カスタムエラークラスを効果的にデバッグでき、コードの品質を高めることができます。次のセクションでは、例外の再スローとカスタムエラーの関係について詳しく説明します。

例外の再スローとカスタムエラー

エラーハンドリングの過程で、例外を再スローすることは重要な技術です。再スローは、エラーのキャッチ後に再度エラーを投げることで、上位の呼び出し元にエラーを伝播させる方法です。カスタムエラーを使用することで、再スローの際に詳細なエラー情報を保持しつつ、エラーを特定しやすくなります。

例外の再スローの基本

再スローは、エラーのキャッチブロック内で行います。以下の例では、カスタムエラーをキャッチし、処理した後に再スローしています。

class CustomError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
        Error.captureStackTrace(this, this.constructor);
    }
}

function riskyOperation() {
    try {
        // エラーが発生する可能性のあるコード
        throw new CustomError("Something went wrong during the operation");
    } catch (error) {
        console.error("Error caught in riskyOperation:", error.message);
        throw error; // 再スロー
    }
}

try {
    riskyOperation();
} catch (error) {
    console.error("Error caught in main:", error.message); // エラーを上位の呼び出し元でキャッチ
}

エラー情報の保持

再スローする際に、元のエラー情報を保持することが重要です。カスタムエラーを使用することで、追加のプロパティを持たせることができます。

class DetailedError extends Error {
    constructor(message, details) {
        super(message);
        this.name = this.constructor.name;
        this.details = details;
        Error.captureStackTrace(this, this.constructor);
    }
}

function performTask() {
    try {
        // エラーが発生する可能性のあるコード
        throw new DetailedError("Task failed", { taskId: 123, userId: 456 });
    } catch (error) {
        console.error("Error caught in performTask:", error.message);
        console.error("Error details:", error.details);
        throw error; // 再スロー
    }
}

try {
    performTask();
} catch (error) {
    console.error("Error caught in main:", error.message); // エラーを上位の呼び出し元でキャッチ
    console.error("Error details in main:", error.details);
}

カスタムエラークラスのチェーン

異なるエラータイプを階層化して再スローする場合、エラーのチェーンを維持することができます。これにより、エラーの発生源と伝播経路を明確に追跡できます。

class NetworkError extends Error {
    constructor(message, url) {
        super(message);
        this.name = this.constructor.name;
        this.url = url;
        Error.captureStackTrace(this, this.constructor);
    }
}

class ApiError extends Error {
    constructor(message, apiName, originalError) {
        super(message);
        this.name = this.constructor.name;
        this.apiName = apiName;
        this.originalError = originalError;
        Error.captureStackTrace(this, this.constructor);
    }
}

async function fetchData(url) {
    try {
        let response = await fetch(url);
        if (!response.ok) {
            throw new NetworkError("Failed to fetch data", url);
        }
        return await response.json();
    } catch (error) {
        throw new ApiError("API request failed", "fetchData", error);
    }
}

fetchData("https://api.example.com/data")
    .then(data => console.log(data))
    .catch(error => {
        console.error("Error caught in main:");
        console.error("Message:", error.message);
        console.error("API Name:", error.apiName);
        if (error.originalError) {
            console.error("Original Error Message:", error.originalError.message);
            console.error("Original Error URL:", error.originalError.url);
        }
    });

これにより、再スローされたエラーの詳細な追跡とデバッグが容易になります。次のセクションでは、カスタムエラークラスのユニットテストについて詳しく説明します。

カスタムエラークラスのユニットテスト

カスタムエラークラスのユニットテストを行うことで、エラーが正しくスローされ、期待通りに機能することを確認できます。ユニットテストは、コードの品質を維持し、エラー処理が意図した通りに動作することを保証します。

ユニットテストの基本

ユニットテストは、コードの最小単位(ユニット)を個別にテストする方法です。カスタムエラークラスのユニットテストでは、エラーのインスタンス化、プロパティの確認、エラーのスローとキャッチの挙動をテストします。以下の例では、Node.js環境で一般的なテストフレームワークであるMochaとアサーションライブラリであるChaiを使用します。

const { expect } = require('chai');

class CustomError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
        Error.captureStackTrace(this, this.constructor);
    }
}

describe('CustomError', () => {
    it('should create an error with the correct message and name', () => {
        const error = new CustomError('Test error');
        expect(error).to.be.an.instanceof(Error);
        expect(error.message).to.equal('Test error');
        expect(error.name).to.equal('CustomError');
    });

    it('should capture stack trace', () => {
        const error = new CustomError('Test error');
        expect(error.stack).to.be.a('string');
        expect(error.stack).to.include('CustomError');
    });

    it('should be throwable and catchable', () => {
        try {
            throw new CustomError('Test error');
        } catch (error) {
            expect(error).to.be.an.instanceof(CustomError);
            expect(error.message).to.equal('Test error');
        }
    });
});

詳細なプロパティのテスト

カスタムエラークラスに追加のプロパティがある場合、それらのプロパティもテストする必要があります。以下の例では、詳細なプロパティを持つエラークラスのテストを示します。

class DetailedError extends Error {
    constructor(message, details) {
        super(message);
        this.name = this.constructor.name;
        this.details = details;
        Error.captureStackTrace(this, this.constructor);
    }
}

describe('DetailedError', () => {
    it('should create an error with the correct message, name, and details', () => {
        const details = { key: 'value' };
        const error = new DetailedError('Test error', details);
        expect(error).to.be.an.instanceof(Error);
        expect(error.message).to.equal('Test error');
        expect(error.name).to.equal('DetailedError');
        expect(error.details).to.deep.equal(details);
    });

    it('should capture stack trace', () => {
        const error = new DetailedError('Test error', { key: 'value' });
        expect(error.stack).to.be.a('string');
        expect(error.stack).to.include('DetailedError');
    });

    it('should be throwable and catchable', () => {
        const details = { key: 'value' };
        try {
            throw new DetailedError('Test error', details);
        } catch (error) {
            expect(error).to.be.an.instanceof(DetailedError);
            expect(error.message).to.equal('Test error');
            expect(error.details).to.deep.equal(details);
        }
    });
});

モックを使用したテスト

エラーがスローされるべき状況をシミュレーションするために、モックを使用することができます。以下の例では、モック関数を使用してエラーが正しくスローされることを確認します。

const sinon = require('sinon');

function riskyFunction() {
    throw new CustomError('Risky function error');
}

describe('riskyFunction', () => {
    it('should throw CustomError', () => {
        expect(() => riskyFunction()).to.throw(CustomError, 'Risky function error');
    });

    it('should call error handler when CustomError is thrown', () => {
        const errorHandler = sinon.spy();
        try {
            riskyFunction();
        } catch (error) {
            errorHandler(error);
        }
        expect(errorHandler.calledOnce).to.be.true;
        expect(errorHandler.firstCall.args[0]).to.be.an.instanceof(CustomError);
    });
});

これらのユニットテストを通じて、カスタムエラークラスが期待通りに機能することを確認できます。次のセクションでは、カスタムエラークラスの実践例について詳しく説明します。

カスタムエラークラスの実践例

ここでは、カスタムエラークラスを実際のプロジェクトでどのように使用するかについて、具体的な実践例をいくつか紹介します。これにより、カスタムエラークラスの有用性と応用範囲を理解することができます。

データベース操作におけるカスタムエラー

データベース操作中に発生する特定のエラーをカスタムエラークラスで処理する方法を示します。以下の例では、データベース接続エラーとクエリエラーを処理するカスタムエラークラスを作成しています。

class DatabaseConnectionError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
        Error.captureStackTrace(this, this.constructor);
    }
}

class QueryError extends Error {
    constructor(message, query) {
        super(message);
        this.name = this.constructor.name;
        this.query = query;
        Error.captureStackTrace(this, this.constructor);
    }
}

function connectToDatabase() {
    // データベース接続の試行
    throw new DatabaseConnectionError("Failed to connect to database");
}

function executeQuery(query) {
    // クエリ実行の試行
    throw new QueryError("Failed to execute query", query);
}

try {
    connectToDatabase();
} catch (error) {
    if (error instanceof DatabaseConnectionError) {
        console.error(`Connection error: ${error.message}`);
    } else {
        throw error; // 再スロー
    }
}

try {
    executeQuery("SELECT * FROM users");
} catch (error) {
    if (error instanceof QueryError) {
        console.error(`Query error: ${error.message}`);
        console.error(`Query: ${error.query}`);
    } else {
        throw error; // 再スロー
    }
}

APIリクエストのエラーハンドリング

APIリクエストにおけるエラー処理にカスタムエラークラスを使用する方法を示します。以下の例では、HTTPエラーを処理するカスタムエラークラスを作成しています。

class HttpError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.name = this.constructor.name;
        this.statusCode = statusCode;
        Error.captureStackTrace(this, this.constructor);
    }
}

async function fetchData(url) {
    try {
        let response = await fetch(url);
        if (!response.ok) {
            throw new HttpError("Failed to fetch data", response.status);
        }
        return await response.json();
    } catch (error) {
        if (error instanceof HttpError) {
            console.error(`HTTP error: ${error.message} (Status: ${error.statusCode})`);
        } else {
            console.error(`Unexpected error: ${error.message}`);
        }
        throw error; // 必要に応じて再スロー
    }
}

// 使用例
fetchData("https://api.example.com/data")
    .then(data => console.log(data))
    .catch(error => console.error("Error occurred:", error));

ユーザー入力のバリデーション

ユーザー入力のバリデーションにおいてカスタムエラークラスを使用する方法を示します。以下の例では、入力フィールドごとにカスタムエラーをスローし、エラーをキャッチして適切なメッセージを表示します。

class ValidationError extends Error {
    constructor(message, field) {
        super(message);
        this.name = this.constructor.name;
        this.field = field;
        Error.captureStackTrace(this, this.constructor);
    }
}

function validateInput(input) {
    if (!input.username) {
        throw new ValidationError("Username is required", "username");
    }
    if (input.password.length < 6) {
        throw new ValidationError("Password must be at least 6 characters long", "password");
    }
    return true;
}

// 使用例
try {
    let input = { username: "", password: "12345" };
    validateInput(input);
} catch (error) {
    if (error instanceof ValidationError) {
        console.error(`Validation error on ${error.field}: ${error.message}`);
    } else {
        console.error(`Unexpected error: ${error.message}`);
    }
}

ログイン認証のエラーハンドリング

ログイン認証プロセスで発生するエラーをカスタムエラークラスで処理する方法を示します。以下の例では、認証エラーを処理するカスタムエラークラスを作成しています。

class AuthenticationError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
        Error.captureStackTrace(this, this.constructor);
    }
}

function authenticateUser(username, password) {
    const validUsername = "user";
    const validPassword = "password";

    if (username !== validUsername || password !== validPassword) {
        throw new AuthenticationError("Invalid username or password");
    }
    return true;
}

// 使用例
try {
    authenticateUser("user", "wrongpassword");
} catch (error) {
    if (error instanceof AuthenticationError) {
        console.error(`Authentication error: ${error.message}`);
    } else {
        console.error(`Unexpected error: ${error.message}`);
    }
}

これらの実践例を通じて、カスタムエラークラスがどのようにプロジェクトに役立つかを理解できるでしょう。次のセクションでは、本記事の要点をまとめます。

まとめ

本記事では、JavaScriptにおけるカスタムエラークラスの作成方法とその利用法について詳細に解説しました。カスタムエラークラスを使用することで、エラーの識別が容易になり、エラーハンドリングが効率的かつ効果的に行えるようになります。

まず、JavaScriptの基本的なエラークラスについて理解し、その上でカスタムエラークラスの作成方法を学びました。さらに、カスタムエラークラスの拡張や具体的な利用方法、エラー処理のベストプラクティス、デバッグ方法についても詳しく説明しました。

実際のプロジェクトにおいては、データベース操作、APIリクエスト、ユーザー入力のバリデーション、ログイン認証など、さまざまなシナリオでカスタムエラークラスを活用することができます。また、ユニットテストを通じて、エラー処理が期待通りに機能することを確認することも重要です。

カスタムエラークラスを効果的に利用することで、コードの信頼性とメンテナンス性を向上させることができるでしょう。これからのプロジェクトで、ぜひカスタムエラークラスを活用してみてください。

コメント

コメントする

目次
  1. エラークラスの基礎
    1. Errorクラスの基本的な使い方
    2. Errorクラスのプロパティ
    3. 組み込みエラークラス
  2. カスタムエラークラスの作成
    1. 基本的なカスタムエラークラスの作成方法
    2. カスタムエラークラスにプロパティを追加する
    3. カスタムエラークラスの利点
  3. カスタムエラークラスの拡張
    1. カスタムエラークラスの階層化
    2. カスタムエラークラスのメソッド追加
    3. カスタムエラークラスのシリアライズ
  4. カスタムエラークラスの利用方法
    1. APIリクエストでのカスタムエラー
    2. フォームバリデーションでのカスタムエラー
    3. カスタムエラーのロギング
  5. エラー処理のベストプラクティス
    1. 特定のエラーをキャッチする
    2. 意味のあるエラーメッセージを提供する
    3. エラー情報のロギング
    4. 再スローの使用
    5. カスタムエラークラスの統一
  6. カスタムエラークラスのデバッグ
    1. エラーメッセージとスタックトレースの活用
    2. ブラウザのデベロッパーツール
    3. Node.jsのデバッグ方法
    4. エラーロギングサービスの利用
    5. エラーハンドリングのテスト
  7. 例外の再スローとカスタムエラー
    1. 例外の再スローの基本
    2. エラー情報の保持
    3. カスタムエラークラスのチェーン
  8. カスタムエラークラスのユニットテスト
    1. ユニットテストの基本
    2. 詳細なプロパティのテスト
    3. モックを使用したテスト
  9. カスタムエラークラスの実践例
    1. データベース操作におけるカスタムエラー
    2. APIリクエストのエラーハンドリング
    3. ユーザー入力のバリデーション
    4. ログイン認証のエラーハンドリング
  10. まとめ