Skip to content

Instantly share code, notes, and snippets.

@manzt
Last active January 7, 2025 16:06
Show Gist options
  • Save manzt/3702f19abb714e21c22ce48851c75abf to your computer and use it in GitHub Desktop.
Save manzt/3702f19abb714e21c22ce48851c75abf to your computer and use it in GitHub Desktop.

Revisions

  1. manzt revised this gist Jan 7, 2025. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions except.ts
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    // Copyright (c) 2024 Trevor Manz - MIT License

    /**
    * Type-safe error handling utilities inspired by Python's try/except.
    *
  2. manzt created this gist Jan 7, 2025.
    72 changes: 72 additions & 0 deletions except.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    /**
    * Type-safe error handling utilities inspired by Python's try/except.
    *
    * @module
    */

    // deno-lint-ignore no-explicit-any
    type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never;

    // deno-lint-ignore no-explicit-any
    type UnionInstanceType<T extends readonly (new (...args: any[]) => any)[]> =
    InstanceType<T[number]>;

    /**
    * Ensures an error matches expected type(s), otherwise rethrows.
    *
    * Unmatched errors bubble up, like Python's `except`. Narrows error types for
    * type-safe property access.
    *
    * @example Catch specific error
    * ```ts
    * class NotFoundError extends Error {
    * getStatus() { return 404 }
    * }
    *
    * try {
    * // This will bubble up since it's not a NotFoundError
    * throw new Error("Unexpected error");
    * } catch (err) {
    * except(err, NotFoundError);
    * // Only NotFoundError reaches here
    * err.getStatus();
    * }
    * ```
    *
    * @example Handle multiple error types
    * ```ts
    * class DbError extends Error {
    * query: string;
    * }
    * class NetworkError extends Error {
    * code: number;
    * }
    *
    * try {
    * await db.query();
    * } catch (err) {
    * except(err, DbError, NetworkError);
    * // Only DbError or NetworkError reach here
    * if (err instanceof DbError) {
    * console.log(err.query);
    * } else {
    * console.log(err.code);
    * }
    * }
    * ```
    *
    * @param error - The error to check
    * @param errorClasses - Expected error type(s)
    * @throws The original error if it doesn't match expected type(s)
    */
    export function except<
    // deno-lint-ignore no-explicit-any
    ErrorClasses extends readonly (new (...args: any[]) => Error)[],
    >(
    error: unknown,
    ...errorClasses: ErrorClasses
    ): asserts error is UnionInstanceType<ErrorClasses> {
    if (!errorClasses.some((ErrorClass) => error instanceof ErrorClass)) {
    throw error;
    }
    }