Skip to content

Commit 2e92bb0

Browse files
committed
feat: adds sync function support
1 parent a051ad6 commit 2e92bb0

File tree

3 files changed

+66
-6
lines changed

3 files changed

+66
-6
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"lint:fix": "eslint . --fix",
4141
"pkg:changeset": "changeset",
4242
"pkg:version": "changeset version",
43-
"pkg:publish": "changeset publish",
43+
"pkg:publish": "bun run build && changeset publish",
4444
"serve": "vite preview",
4545
"start": "vite",
4646
"test": "vitest run",

src/__tests__/tryCatch.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,42 @@ import { describe, expect, it } from "vitest";
33
import { tryCatch } from "../index";
44

55
describe("UTIL FUNCTION: tryCatch", () => {
6+
// Sync values
7+
it("handles sync functions that return values", async () => {
8+
const [err, data] = await tryCatch(() => JSON.parse('{"a":1}'));
9+
expect(err).toBeNull();
10+
expect(data).toEqual({ a: 1 });
11+
});
12+
13+
it("handles sync function that throws", async () => {
14+
const [err, data] = await tryCatch(() => JSON.parse("{ a: 1 }")); // invalid JSON (unquoted key)
15+
expect(err).toBeInstanceOf(SyntaxError);
16+
expect(err?.message).toContain(
17+
"Expected property name or '}' in JSON at position 2",
18+
);
19+
expect(data).toBeNull();
20+
});
21+
22+
it("handles sync functions that throw errors", async () => {
23+
const [err, data] = await tryCatch(() => {
24+
throw new RangeError("Out of bounds");
25+
});
26+
expect(err).toBeInstanceOf(RangeError);
27+
expect(err?.message).toBe("Out of bounds");
28+
expect(data).toBeNull();
29+
});
30+
31+
it("handles sync functions that throw non-Error values", async () => {
32+
const [err, data] = await tryCatch(() => {
33+
throw "sync oops";
34+
});
35+
expect(err).toBeInstanceOf(Error);
36+
expect(err?.message).toBe("sync oops");
37+
expect(data).toBeNull();
38+
});
39+
40+
// Async values
41+
642
it("handles resolved promises correctly", async () => {
743
const [err, data] = await tryCatch(Promise.resolve(42));
844
expect(err).toBeNull();

src/index.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,41 @@ type Success<T> = [error: null, data: T];
22
type Failure<E extends Error = Error> = [error: E, data: null];
33
type Result<T, E extends Error = Error> = Success<T> | Failure<E>;
44

5+
type SyncFn<T> = () => T;
6+
type AsyncFn<T> = () => Promise<T>;
7+
8+
/**
9+
* Acceptable input types for `tryCatch`:
10+
* - A synchronous function that might throw
11+
* - An async function (returns a promise)
12+
* - A promise (e.g. from `fetch()`)
13+
*/
14+
type TryCatchInput<T> = SyncFn<T> | AsyncFn<T> | Promise<T>;
15+
516
/**
6-
* Wraps a promise or async function and returns a tuple [error, data].
7-
* Catches both synchronous and asynchronous errors.
17+
* Wraps a potentially throwing function or a promise and returns a tuple `[error, data]`.
18+
* Catches both sync and async errors, and wraps non-Error throws into an `Error` object.
19+
*
20+
* ## Examples
21+
*
22+
* ```ts
23+
* // ✅ Synchronous function that may throw
24+
* const [err, data] = await tryCatch(() => JSON.parse("{ \"a\": 1 }"));
25+
*
26+
* // ✅ Async function
27+
* const [err, data] = await tryCatch(() => fetch("/api/data").then(r => r.json()));
28+
*
29+
* // ✅ Direct promise
30+
* const [err, data] = await tryCatch(fetch("/api/data"));
31+
* ```
832
*/
933
export async function tryCatch<T, E extends Error = Error>(
10-
promiseOrFn: Promise<T> | (() => Promise<T>),
34+
input: TryCatchInput<T>,
1135
): Promise<Result<T, E>> {
1236
try {
13-
const data = typeof promiseOrFn === "function" ? await promiseOrFn() : await promiseOrFn;
37+
const result = typeof input === "function" ? await input() : await input;
1438

15-
return [null, data];
39+
return [null, result];
1640
} catch (e) {
1741
const error = e instanceof Error ? e : new Error(String(e), { cause: e });
1842
return [error as E, null];

0 commit comments

Comments
 (0)