Skip to content

Commit e990d74

Browse files
authored
Merge pull request #10 from maxmorozoff/try-catch-tuple/dev
Refactor and Document TryCatch Types
2 parents b26904c + 73951e0 commit e990d74

File tree

5 files changed

+101
-18
lines changed

5 files changed

+101
-18
lines changed

.changeset/shy-trees-lose.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
"@maxmorozoff/try-catch-tuple": patch
3+
---
4+
5+
docs:
6+
7+
- Add JSDoc for TryCatch types
8+
9+
chore:
10+
11+
- Export missing types
12+
- Update default error type
13+
- Update Branded type
14+
- Update DataErrorTuple type (e.g., `rest` is now `never[]`)
15+
- Restrict array methods on DataErrorTuple
16+
- Make `fn` parameter required
17+
18+
refactor:
19+
20+
- Extract DataErrorTuple to type alias

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ type Result<T, E = Error> = ([data: T, error: null] | [data: null, error: E]) &
483483
### Edge Cases
484484
485485
```ts
486-
tryCatch(); // Returns [undefined, null]
486+
tryCatch(undefined); // Returns [undefined, null]
487487
tryCatch(null); // Returns [null, null]
488488
tryCatch(() => {
489489
throw new Error("Unexpected Error");

packages/try-catch-tuple/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ type Result<T, E = Error> = [data: T, error: null] | [data: null, error: E];
266266
## Edge Cases
267267
268268
```ts
269-
tryCatch(); // Returns [undefined, null]
269+
tryCatch(undefined); // Returns [undefined, null]
270270
tryCatch(null); // Returns [null, null]
271271
tryCatch(() => {
272272
throw new Error("Unexpected Error");

packages/try-catch-tuple/src/tryCatch.ts

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,104 @@
1-
type Branded<T> = T & { __tryCatchTupleResult: any };
2-
type Success<T> = Branded<[data: T, error: null]>;
3-
type Failure<E> = Branded<[data: null, error: E | Error]>;
4-
type Result<T, E> = Success<T> | Failure<E>;
1+
type Branded<T> = T & { __tryCatchTupleResult: never };
2+
type DisableArrayMethods<T> = T & {
3+
[K in Exclude<keyof Array<any>, "length" | symbol>]: never;
4+
};
5+
6+
type DataErrorTuple<T, E> = Branded<
7+
DisableArrayMethods<[data: T, error: E] & never[]>
8+
>;
9+
10+
/**
11+
* Represents a successful result where `data` is present and `error` is `null`.
12+
*/
13+
export type Success<T> = DataErrorTuple<T, null>;
14+
15+
/**
16+
* Represents a failure result where `error` contains an error instance and `data` is `null`.
17+
*/
18+
export type Failure<E extends Error> = DataErrorTuple<null, E | Error>;
19+
20+
/**
21+
* Represents the result of an operation that can either succeed with `T` or fail with `E`.
22+
*/
23+
export type Result<T, E extends Error> = Success<T> | Failure<E>;
524

6-
type TryCatchResult<T, E> = T extends Promise<infer U>
25+
/**
26+
* Resolves the return type based on whether `T` is a promise:
27+
* - If `T` is a `Promise<U>`, returns `Promise<Result<U, E>>`.
28+
* - Otherwise, returns `Result<T, E>`.
29+
*/
30+
export type TryCatchResult<T, E extends Error> = T extends Promise<infer U>
731
? Promise<Result<U, E>>
832
: Result<T, E>;
933

10-
type TryCatchFunc<E_ extends Error = never> = <T, E extends Error = E_>(
11-
fn?: T | (() => T),
34+
/**
35+
* Function type for handling try-catch logic.
36+
*
37+
* @template E_ Default error type.
38+
* @template T The return type of the function being executed.
39+
* @template E The error type that extends the default error type.
40+
*
41+
* @param fn A function, promise, or value to execute within a try-catch block.
42+
* @param operationName Optional name added to `error.message` for better debugging and context.
43+
*/
44+
export type TryCatchFunc<E_ extends Error = Error> = <T, E extends Error = E_>(
45+
fn: T | (() => T),
1246
operationName?: string,
1347
) => TryCatchResult<T, E>;
1448

15-
type TryCatch<
49+
/**
50+
* A utility for handling synchronous and asynchronous operations within a try-catch block.
51+
*
52+
* @template F The function type for try-catch execution.
53+
* @template E_ The base error type.
54+
*/
55+
export type TryCatch<
1656
F extends TryCatchFunc = TryCatchFunc,
17-
E_ extends Error = never,
57+
E_ extends Error = Error,
1858
> = F & {
59+
/**
60+
* Executes a synchronous function inside a try-catch block.
61+
*
62+
* @param fn The function to execute.
63+
* @param operationName Optional name added to `error.message` for better debugging and context.
64+
* @returns A `Result<T, E>` indicating success or failure.
65+
*/
1966
sync: <T, E extends Error = E_>(
2067
fn: () => T,
2168
operationName?: string,
2269
) => Result<T, E>;
70+
71+
/**
72+
* Executes an asynchronous function inside a try-catch block.
73+
*
74+
* @param fn The function or promise to execute.
75+
* @param operationName Optional name added to `error.message` for better debugging and context.
76+
* @returns A `Promise<Result<T, E>>` indicating success or failure.
77+
*/
2378
async: <T, E extends Error = E_>(
2479
fn: Promise<T> | (() => Promise<T>),
2580
operationName?: string,
2681
) => Promise<Result<T, E>>;
82+
83+
/**
84+
* Creates a new `TryCatch` instance that handles additional error types.
85+
*
86+
* @template E Extends the existing error type.
87+
* @returns A new `TryCatch` instance with extended error handling capabilities.
88+
*/
2789
errors: <E extends Error>() => TryCatch<TryCatchFunc<E | E_>, E | E_>;
2890
};
2991

3092
/**
3193
* tryCatch - Error handling that can be synchronous or asynchronous
3294
* based on the input function.
3395
*
34-
* @param fn Function to execute, Promise or value.
35-
* @param operationName Optional name for context.
96+
* @param fn A function, promise, or value to execute within a try-catch block.
97+
* @param operationName Optional name added to `error.message` for better debugging and context.
3698
* @returns A Result, or a Promise resolving to a Result, depending on fn.
3799
*/
38-
export const tryCatch: TryCatch = <T, E extends Error = never>(
39-
fn?: T | (() => T),
100+
export const tryCatch: TryCatch = <T, E extends Error = Error>(
101+
fn: T | (() => T),
40102
operationName?: string,
41103
) => {
42104
try {
@@ -51,7 +113,7 @@ export const tryCatch: TryCatch = <T, E extends Error = never>(
51113
}
52114
};
53115

54-
export const tryCatchSync: TryCatch["sync"] = <T, E extends Error = never>(
116+
export const tryCatchSync: TryCatch["sync"] = <T, E extends Error = Error>(
55117
fn: () => T,
56118
operationName?: string,
57119
) => {
@@ -65,7 +127,7 @@ export const tryCatchSync: TryCatch["sync"] = <T, E extends Error = never>(
65127

66128
export const tryCatchAsync: TryCatch["async"] = async <
67129
T,
68-
E extends Error = never,
130+
E extends Error = Error,
69131
>(
70132
fn: Promise<T> | (() => Promise<T>),
71133
operationName?: string,

packages/try-catch-tuple/tests/tryCatch.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ describe("tryCatch", () => {
344344

345345
describe("edge cases", () => {
346346
test("should handle undefined fn", () => {
347-
const [result, error] = tryCatch();
347+
const [result, error] = tryCatch(undefined);
348+
if (error) expect.unreachable();
348349
expect(result).toBe(undefined);
349350
expect(error).toBeNil();
350351
});

0 commit comments

Comments
 (0)