Skip to content

Commit

Permalink
Add except util for try/catch blocks
Browse files Browse the repository at this point in the history
Add a custom type-safe util that ensures an error matches an expected
type, otherwise rethrows. Just cleans up some logic with fallback logic
and `NodeNotFoundError`.
  • Loading branch information
manzt committed Jan 7, 2025
1 parent c15c1a1 commit 7e4d5af
Showing 3 changed files with 55 additions and 12 deletions.
10 changes: 4 additions & 6 deletions packages/core/src/consolidated.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import type {
GroupMetadata,
GroupMetadataV2,
} from "./metadata.js";
import { json_decode_object, json_encode_object } from "./util.js";
import { except, json_decode_object, json_encode_object } from "./util.js";

type ConsolidatedMetadata = {
metadata: Record<string, ArrayMetadataV2 | GroupMetadataV2>;
@@ -134,10 +134,8 @@ export async function withConsolidated<Store extends Readable>(
export async function tryWithConsolidated<Store extends Readable>(
store: Store,
): Promise<Listable<Store> | Store> {
return withConsolidated(store).catch((e: unknown) => {
if (e instanceof NodeNotFoundError) {
return store;
}
throw e;
return withConsolidated(store).catch((error: unknown) => {
except(error, NodeNotFoundError);
return store;
});
}
11 changes: 5 additions & 6 deletions packages/core/src/open.ts
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import type {
} from "./metadata.js";
import {
ensure_correct_scalar,
except,
json_decode_object,
v2_to_v3_array_metadata,
v2_to_v3_group_metadata,
@@ -64,8 +65,8 @@ async function open_v2<Store extends Readable>(
if (options.kind === "array") return open_array_v2(loc, attrs);
if (options.kind === "group") return open_group_v2(loc, attrs);
return open_array_v2(loc, attrs).catch((err) => {
if (err instanceof NodeNotFoundError) return open_group_v2(loc, attrs);
throw err;
except(err, NodeNotFoundError);
return open_group_v2(loc, attrs);
});
}

@@ -192,10 +193,8 @@ export async function open<Store extends Readable>(
let open_primary = version_max === "v2" ? open.v2 : open.v3;
let open_secondary = version_max === "v2" ? open.v3 : open.v2;
return open_primary(location, options).catch((err) => {
if (err instanceof NodeNotFoundError) {
return open_secondary(location, options);
}
throw err;
except(err, NodeNotFoundError);
return open_secondary(location, options);
});
}

46 changes: 46 additions & 0 deletions packages/core/src/util.ts
Original file line number Diff line number Diff line change
@@ -292,3 +292,49 @@ export function ensure_correct_scalar<D extends DataType>(
}
return metadata.fill_value;
}

// biome-ignore lint/suspicious/noExplicitAny: Necessary for type inference
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never;

// biome-ignore lint/suspicious/noExplicitAny: Necessary for type inference
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.
*
* @see {@link https://gist.github.com/manzt/3702f19abb714e21c22ce48851c75abf}
*
* @example
* ```ts
* class DatabaseError extends Error { }
* class NetworkError extends Error { }
*
* try {
* await db.query();
* } catch (err) {
* except(err, DatabaseError, NetworkError);
* err // DatabaseError | NetworkError
* }
* ```
*
* @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<
ErrorClasses extends readonly (new (
// biome-ignore lint/suspicious/noExplicitAny: Necessary for type inference
...args: any[]
) => Error)[],
>(
error: unknown,
...errorClasses: ErrorClasses
): asserts error is UnionInstanceType<ErrorClasses> {
if (!errorClasses.some((ErrorClass) => error instanceof ErrorClass)) {
throw error;
}
}

0 comments on commit 7e4d5af

Please sign in to comment.