404 Not Found

404 Not Found


nginx

TypeScriptのエラー処理

エラー処理は、あらゆるプロジェクトにおいて不可欠な要素です。TypeScriptの型システムにより、エラー処理はより安全で予測しやすくなります。

1. JavaScriptにおけるエラー処理の基礎

(1) try/catch/finally

TYPESCRIPT
try {
  let data = JSON.parse(input);
  console.log(data);
} catch (error) {
  // error The type is unknown(strict In mode)
  console.log("Parsing Failed");
} finally {
  console.log("Free Up Resources");
  // This will be executed regardless of success or failure
}

(2) throw文

JavaScript では、Error オブジェクトだけでなく、あらゆる値をスローすることができます:

TYPESCRIPT
throw new Error("An error occurred");        // ✅ Standard Practice
throw "An error occurred";                    // ⚠️ It's possible, but not ideal.
throw 404;                         // ⚠️ It's possible, but not ideal.
throw { code: 500, msg: "Server Error" }; // ⚠️ It's possible, but not ideal.
📌 推奨事項: 常に Error オブジェクト(またはそのサブクラスのいずれか)をスローするようにしてください。これにはコールスタック情報が含まれており、デバッグが容易になります。文字列や数値をスローしても、コールスタック情報は得られません。

(3) エラーの種類

TYPESCRIPT
interface Error {
  name: string;     // Error name (e.g., "TypeError", "RangeError")
  message: string;  // Error Description
  stack?: string;   // Call Stack(Non-standard but widely supported)
}

2. 組み込みのエラー型と型の絞り込み

(1) よくある組み込みのエラー

エラーの種類 発生原因
Error 一般的なエラー
TypeError タイプエラー(例:null.toString()
RangeError 値が範囲外(例:再帰的オーバーフロー)
SyntaxError 構文エラー(例:JSON.parse が失敗しました)
ReferenceError 未定義の変数への参照
URIError URIのエンコード/デコードエラー

(2) instanceof によるエラー種類の絞り込み

TYPESCRIPT
function processValue(value: unknown) {
  try {
    let num = Number(value);
    if (isNaN(num)) throw new TypeError("Not a valid number");
    if (num < 0) throw new RangeError("Cannot be a negative number");
    return num;
  } catch (error) {
    if (error instanceof TypeError) {
      console.log(`Type error:${error.message}`);  // ✅ narrowed to TypeError
    } else if (error instanceof RangeError) {
      console.log(`Range error:${error.message}`);  // ✅ narrowed to RangeError
    } else if (error instanceof Error) {
      console.log(`Other Errors:${error.message}`);  // ✅ narrowed to Error
    } else {
      // In theory, it won't reach there.——JavaScript Always throw Error or its subclasses
      console.log("Unknown error");
    }
  }
}

3. カスタムエラークラス

(1) 基本的なカスタムエラー

TYPESCRIPT
class AppError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "AppError";  // Incorrect setting name
  }
}

class ValidationError extends AppError {
  constructor(
    message: string,
    public field: string    // Additional error messages
  ) {
    super(message);
    this.name = "ValidationError";
  }
}

class NotFoundError extends AppError {
  constructor(
    public resource: string,
    public id: number | string
  ) {
    super(`${resource} (${id}) Does not exist`);
    this.name = "NotFoundError";
  }
}

(2) カスタムエラーの使用

TYPESCRIPT
function findUser(id: number) {
  if (id <= 0) {
    throw new ValidationError("IDMust be a positive number", "id");
  }
  // Simulated Search
  if (id > 100) {
    throw new NotFoundError("User", id);
  }
  return { id, name: "User" + id };
}

try {
  let user = findUser(999);
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(`Verification Failed:Field ${error.field},${error.message}`);
  } else if (error instanceof NotFoundError) {
    console.log(`Not found:${error.resource},ID=${error.id}`);
  } else if (error instanceof Error) {
    console.log(`Error:${error.message}`);
  }
}

▶ 例:HTTPエラーシステム

TYPESCRIPT
class HttpError extends Error {
  constructor(
    public statusCode: number,
    message: string
  ) {
    super(message);
    this.name = "HttpError";
  }
}

class BadRequestError extends HttpError {
  constructor(message: string) {
    super(400, message);
    this.name = "BadRequestError";
  }
}

class UnauthorizedError extends HttpError {
  constructor(message: string = "Unauthorized") {
    super(401, message);
    this.name = "UnauthorizedError";
  }
}

class ForbiddenError extends HttpError {
  constructor(message: string = "Access Denied") {
    super(403, message);
    this.name = "ForbiddenError";
  }
}

class NotFoundError2 extends HttpError {
  constructor(resource: string) {
    super(404, `${resource} Does not exist`);
    this.name = "NotFoundError";
  }
}

// Usage
function handleRequest(path: string) {
  if (!path.startsWith("/api/")) {
    throw new BadRequestError("The path must begin with /api/ Introduction");
  }
  if (path === "/api/admin") {
    throw new ForbiddenError();
  }
  return { data: "Response Data" };
}

try {
  let result = handleRequest("/api/users");
  console.log(result.data);
} catch (error) {
  if (error instanceof HttpError) {
    console.log(`HTTP ${error.statusCode}: ${error.message}`);
  }
}

出力:

TEXT
Response Data

4. 結果のパターン

try/catch の問題点は、「例外が暗黙的」であることです。つまり、関数のシグネチャからは、例外がスローされる可能性があることがわかりません。Result パターンでは、エラーを戻り値の一部として扱います:

(1) 結果の型を定義する

TYPESCRIPT
type Success<T> = { ok: true; value: T };
type Failure<E> = { ok: false; error: E };
type Result<T, E = Error> = Success<T> | Failure<E>;

(2) 結果の作成

TYPESCRIPT
function success<T>(value: T): Success<T> {
  return { ok: true, value };
}

function failure<E>(error: E): Failure<E> {
  return { ok: false, error };
}

(3) try/catch の代わりに Result を使用する

TYPESCRIPT
function divide(a: number, b: number): Result<number, string> {
  if (b === 0) {
    return failure("The divisor cannot be zero.");
  }
  return success(a / b);
}

let result1 = divide(10, 2);
let result2 = divide(10, 0);

if (result1.ok) {
  console.log(result1.value);  // 5 ✅
} else {
  console.log(result1.error);
}

if (result2.ok) {
  console.log(result2.value);
} else {
  console.log(result2.error);  // "The divisor cannot be zero." ✅
}

(4) 結果:ツールベースの手法

TYPESCRIPT
function tryCatch<T>(fn: () => T): Result<T, Error> {
  try {
    return success(fn());
  } catch (error) {
    return failure(error instanceof Error ? error : new Error(String(error)));
  }
}

async function tryAsync<T>(fn: () => Promise<T>): Promise<Result<T, Error>> {
  try {
    return success(await fn());
  } catch (error) {
    return failure(error instanceof Error ? error : new Error(String(error)));
  }
}

// Usage
let parseResult = tryCatch(() => JSON.parse('{"name":"Charlie"}'));
if (parseResult.ok) {
  console.log(parseResult.value.name);  // "Charlie"
}

5. catch ブロックにおけるエラータイプの処理

(1) 厳密モードでは、エラーは不明です

TYPESCRIPT
try {
  JSON.parse("invalid");
} catch (error) {
  // error The type is unknown(TypeScript 4.4+)
  // console.log(error.message);  // ❌ unknown NonemessageProperties

  // ✅ Method 1:instanceof Inspection
  if (error instanceof Error) {
    console.log(error.message);
  }

  // ✅ Method 2:Type Assertion(Caution)
  let msg = (error as Error).message;

  // ✅ Method 3:Type Guard Function
  function getErrorMessage(error: unknown): string {
    if (error instanceof Error) return error.message;
    if (typeof error === "string") return error;
    return "Unknown error";
  }
  console.log(getErrorMessage(error));
}

(2) 旧バージョンのTypeScriptにおけるerror

TYPESCRIPT
// TypeScript 4.3 and earlier——catch 's error The type is any
// You can tsconfig Useful useUnknownInCatchVariables: true Change to unknown

// TypeScript 4.4+——Default unknown(strict In mode)

❓ よくある質問

Q try/catch と Result パターンのどちらを使うべきですか?
A この 2 つのアプローチは互いに排他的ではありません。try/catchはJavaScript/TypeScriptにおける標準的な例外処理メカニズムであり、予測不可能な実行時エラー(ネットワーク障害やJSONの解析エラーなど)に適しています。一方、Resultパターンは予測可能なビジネスエラーに適しています。関数のシグネチャで「失敗の可能性がある」ことが明示されており、呼び出し側がそれを処理する必要があります。プロジェクト全体で一貫したスタイルを維持するようにしてください。
Q なぜエラーは Error ではなく catch unknown によって捕捉されるのですか?
A JavaScript では任意の値をスローできるためです。つまり、throw "message"throw 42 も有効な値です。TypeScriptでは、catchでキャッチされる値がErrorのインスタンスであることを保証できないため、unknownを使用するのが最も安全な方法です。実際の開発では、スローされる値の99%はErrorオブジェクトであるため、instanceof Errorを使用することで型を絞り込むことができます。
Q カスタムエラークラスは super を呼び出す必要がありますか?
A はい、必ず呼び出す必要があります。カスタムエラークラスが Error を継承する場合、コンストラクタ内で super(message) を呼び出し、Errormessage プロパティを初期化する必要があります。また、this.name を手動で設定することを推奨します。そうしないと、name はデフォルトで親クラスの名前に設定されます。
Q finally ブロック内のコードは戻り値に影響しますか?
A finally ブロック内の return ステートメントは、try/catch ブロック内の return ステートメントを上書きします。これはバグのよくある原因です。finallyブロックは、リソースのクリーンアップ(ファイルの閉じ処理、ロックの解放など)にのみ使用し、そこにreturn文を含めないことを推奨します。

📖 まとめ

📝 練習問題

  1. 基本問題(難易度 ⭐):関数 safeParse(json: string): Result<object, Error> を記述してください。この関数は、JSON.parsetry/catch ブロックでラップし、成功した場合は success を、失敗した場合は failure を返すものです。
  2. 上級問題(難易度 ⭐⭐):基底クラス AppError と、サブクラス DatabaseErrorquery プロパティを持つ)および AuthErrorstatusCode プロパティを持つ)を定義してください。エラーの種類に応じて、異なるHTTPステータスコードとメッセージを返す関数を作成してください。
  3. 課題(難易度:⭐⭐⭐)tryAsync<T>(fn: () => Promise<T>): Promise<Result<T, AppError>> を実装し、非同期操作で発生した例外を AppError システムに統一的に変換するようにします(ネットワークエラー、タイムアウトエラー、サーバーエラーを区別します)。失敗した場合に自動的に再試行を行う fetchWithRetry 関数を記述してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%