From e26c8337efb6263e088a21393fa494d6d89331ad Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 12:40:45 +0200 Subject: [PATCH 1/9] chore: update default error type --- packages/try-catch-tuple/src/tryCatch.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index 26e8f0e..0db58ee 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -1,20 +1,20 @@ type Branded = T & { __tryCatchTupleResult: any }; type Success = Branded<[data: T, error: null]>; -type Failure = Branded<[data: null, error: E | Error]>; -type Result = Success | Failure; +type Failure = Branded<[data: null, error: E | Error]>; +type Result = Success | Failure; -type TryCatchResult = T extends Promise +type TryCatchResult = T extends Promise ? Promise> : Result; -type TryCatchFunc = ( +type TryCatchFunc = ( fn?: T | (() => T), operationName?: string, ) => TryCatchResult; type TryCatch< F extends TryCatchFunc = TryCatchFunc, - E_ extends Error = never, + E_ extends Error = Error, > = F & { sync: ( fn: () => T, @@ -35,7 +35,7 @@ type TryCatch< * @param operationName Optional name for context. * @returns A Result, or a Promise resolving to a Result, depending on fn. */ -export const tryCatch: TryCatch = ( +export const tryCatch: TryCatch = ( fn?: T | (() => T), operationName?: string, ) => { @@ -51,7 +51,7 @@ export const tryCatch: TryCatch = ( } }; -export const tryCatchSync: TryCatch["sync"] = ( +export const tryCatchSync: TryCatch["sync"] = ( fn: () => T, operationName?: string, ) => { @@ -65,7 +65,7 @@ export const tryCatchSync: TryCatch["sync"] = ( export const tryCatchAsync: TryCatch["async"] = async < T, - E extends Error = never, + E extends Error = Error, >( fn: Promise | (() => Promise), operationName?: string, From 9ed0ab024ac75b9f063cfee2eaea5c2b222b9420 Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 12:44:51 +0200 Subject: [PATCH 2/9] chore: make fn parameter required --- README.md | 2 +- packages/try-catch-tuple/README.md | 2 +- packages/try-catch-tuple/src/tryCatch.ts | 4 ++-- packages/try-catch-tuple/tests/tryCatch.test.ts | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index adc7b89..c46bef9 100644 --- a/README.md +++ b/README.md @@ -483,7 +483,7 @@ type Result = ([data: T, error: null] | [data: null, error: E]) & ### Edge Cases ```ts -tryCatch(); // Returns [undefined, null] +tryCatch(undefined); // Returns [undefined, null] tryCatch(null); // Returns [null, null] tryCatch(() => { throw new Error("Unexpected Error"); diff --git a/packages/try-catch-tuple/README.md b/packages/try-catch-tuple/README.md index b58b015..409ed14 100644 --- a/packages/try-catch-tuple/README.md +++ b/packages/try-catch-tuple/README.md @@ -266,7 +266,7 @@ type Result = [data: T, error: null] | [data: null, error: E]; ## Edge Cases ```ts -tryCatch(); // Returns [undefined, null] +tryCatch(undefined); // Returns [undefined, null] tryCatch(null); // Returns [null, null] tryCatch(() => { throw new Error("Unexpected Error"); diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index 0db58ee..e5e2ad1 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -8,7 +8,7 @@ type TryCatchResult = T extends Promise : Result; type TryCatchFunc = ( - fn?: T | (() => T), + fn: T | (() => T), operationName?: string, ) => TryCatchResult; @@ -36,7 +36,7 @@ type TryCatch< * @returns A Result, or a Promise resolving to a Result, depending on fn. */ export const tryCatch: TryCatch = ( - fn?: T | (() => T), + fn: T | (() => T), operationName?: string, ) => { try { diff --git a/packages/try-catch-tuple/tests/tryCatch.test.ts b/packages/try-catch-tuple/tests/tryCatch.test.ts index 553afd3..6297e87 100644 --- a/packages/try-catch-tuple/tests/tryCatch.test.ts +++ b/packages/try-catch-tuple/tests/tryCatch.test.ts @@ -344,7 +344,8 @@ describe("tryCatch", () => { describe("edge cases", () => { test("should handle undefined fn", () => { - const [result, error] = tryCatch(); + const [result, error] = tryCatch(undefined); + if (error) expect.unreachable(); expect(result).toBe(undefined); expect(error).toBeNil(); }); From 7dfe927dfce907cc68540b9421f7b226c52414fc Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 12:47:52 +0200 Subject: [PATCH 3/9] refactor: extract DataErrorTuple to type alias --- packages/try-catch-tuple/src/tryCatch.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index e5e2ad1..c2f5e98 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -1,6 +1,11 @@ type Branded = T & { __tryCatchTupleResult: any }; -type Success = Branded<[data: T, error: null]>; -type Failure = Branded<[data: null, error: E | Error]>; + +type DataErrorTuple = Branded< + [data: T, error: E] +>; + +type Success = DataErrorTuple; +type Failure = DataErrorTuple; type Result = Success | Failure; type TryCatchResult = T extends Promise From 7c3e89297b84cdcb245e1033ec880aabe10cf050 Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 12:49:51 +0200 Subject: [PATCH 4/9] chore: update Branded type --- packages/try-catch-tuple/src/tryCatch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index c2f5e98..d0689e2 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -1,4 +1,4 @@ -type Branded = T & { __tryCatchTupleResult: any }; +type Branded = T & { __tryCatchTupleResult: never }; type DataErrorTuple = Branded< [data: T, error: E] From 9b4eb3ec6800721f6596ae347e6931a82f590e2c Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 12:57:46 +0200 Subject: [PATCH 5/9] chore: update DataErrorTuple type This improves: - The `rest` type `[res, err, ...rest]` is now `never[]`. - The `value` type in `tryCatch(...).map((value) => {})` is now `never`. --- packages/try-catch-tuple/src/tryCatch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index d0689e2..4ee94ef 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -1,7 +1,7 @@ type Branded = T & { __tryCatchTupleResult: never }; type DataErrorTuple = Branded< - [data: T, error: E] + [data: T, error: E] & never[] >; type Success = DataErrorTuple; From db99a2d8da5831754f243b3130ea16ff887148ee Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 13:05:43 +0200 Subject: [PATCH 6/9] chore: restrict array methods on DataErrorTuple This improves: - Strengthens type safety by preventing unintended array operations. See: https://github.com/microsoft/TypeScript/issues/xyz for more context. --- packages/try-catch-tuple/src/tryCatch.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index 4ee94ef..8a31456 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -1,7 +1,10 @@ type Branded = T & { __tryCatchTupleResult: never }; +type DisableArrayMethods = T & { + [K in Exclude, "length" | symbol>]: never; +}; type DataErrorTuple = Branded< - [data: T, error: E] & never[] + DisableArrayMethods<[data: T, error: E] & never[]> >; type Success = DataErrorTuple; From 5c990b6425735652f54e3edad69d69d2f0011bde Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 13:09:08 +0200 Subject: [PATCH 7/9] chore: export missing types --- packages/try-catch-tuple/src/tryCatch.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index 8a31456..cc80a16 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -7,20 +7,20 @@ type DataErrorTuple = Branded< DisableArrayMethods<[data: T, error: E] & never[]> >; -type Success = DataErrorTuple; -type Failure = DataErrorTuple; -type Result = Success | Failure; +export type Success = DataErrorTuple; +export type Failure = DataErrorTuple; +export type Result = Success | Failure; -type TryCatchResult = T extends Promise +export type TryCatchResult = T extends Promise ? Promise> : Result; -type TryCatchFunc = ( +export type TryCatchFunc = ( fn: T | (() => T), operationName?: string, ) => TryCatchResult; -type TryCatch< +export type TryCatch< F extends TryCatchFunc = TryCatchFunc, E_ extends Error = Error, > = F & { From 2ef8f6dcaf6b75913c308e45f236c60a98258e7c Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 13:23:52 +0200 Subject: [PATCH 8/9] docs: add JSDoc for TryCatch types This improves: - Provides clear descriptions for `Success`, `Failure`, `Result`, and related types. - Documents generic parameters and return types for better type clarity. - Enhances developer experience with detailed function annotations. --- packages/try-catch-tuple/src/tryCatch.ts | 58 +++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/try-catch-tuple/src/tryCatch.ts b/packages/try-catch-tuple/src/tryCatch.ts index cc80a16..83437c3 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -7,31 +7,85 @@ type DataErrorTuple = Branded< DisableArrayMethods<[data: T, error: E] & never[]> >; +/** + * Represents a successful result where `data` is present and `error` is `null`. + */ export type Success = DataErrorTuple; + +/** + * Represents a failure result where `error` contains an error instance and `data` is `null`. + */ export type Failure = DataErrorTuple; + +/** + * Represents the result of an operation that can either succeed with `T` or fail with `E`. + */ export type Result = Success | Failure; +/** + * Resolves the return type based on whether `T` is a promise: + * - If `T` is a `Promise`, returns `Promise>`. + * - Otherwise, returns `Result`. + */ export type TryCatchResult = T extends Promise ? Promise> : Result; +/** + * Function type for handling try-catch logic. + * + * @template E_ Default error type. + * @template T The return type of the function being executed. + * @template E The error type that extends the default error type. + * + * @param fn A function, promise, or value to execute within a try-catch block. + * @param operationName Optional name added to `error.message` for better debugging and context. + */ export type TryCatchFunc = ( fn: T | (() => T), operationName?: string, ) => TryCatchResult; +/** + * A utility for handling synchronous and asynchronous operations within a try-catch block. + * + * @template F The function type for try-catch execution. + * @template E_ The base error type. + */ export type TryCatch< F extends TryCatchFunc = TryCatchFunc, E_ extends Error = Error, > = F & { + /** + * Executes a synchronous function inside a try-catch block. + * + * @param fn The function to execute. + * @param operationName Optional name added to `error.message` for better debugging and context. + * @returns A `Result` indicating success or failure. + */ sync: ( fn: () => T, operationName?: string, ) => Result; + + /** + * Executes an asynchronous function inside a try-catch block. + * + * @param fn The function or promise to execute. + * @param operationName Optional name added to `error.message` for better debugging and context. + * @returns A `Promise>` indicating success or failure. + */ async: ( fn: Promise | (() => Promise), operationName?: string, ) => Promise>; + + /** + * Creates a new `TryCatch` instance that handles additional error types. + * + * @template E Extends the existing error type. + * @returns A new `TryCatch` instance with extended error handling capabilities. + */ errors: () => TryCatch, E | E_>; }; @@ -39,8 +93,8 @@ export type TryCatch< * tryCatch - Error handling that can be synchronous or asynchronous * based on the input function. * - * @param fn Function to execute, Promise or value. - * @param operationName Optional name for context. + * @param fn A function, promise, or value to execute within a try-catch block. + * @param operationName Optional name added to `error.message` for better debugging and context. * @returns A Result, or a Promise resolving to a Result, depending on fn. */ export const tryCatch: TryCatch = ( From 73951e0e8c7af5049bc24d88b5a5e9a1071708cb Mon Sep 17 00:00:00 2001 From: Max Morozov Date: Tue, 1 Apr 2025 13:38:08 +0200 Subject: [PATCH 9/9] chore: add changeset --- .changeset/shy-trees-lose.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .changeset/shy-trees-lose.md diff --git a/.changeset/shy-trees-lose.md b/.changeset/shy-trees-lose.md new file mode 100644 index 0000000..6cae155 --- /dev/null +++ b/.changeset/shy-trees-lose.md @@ -0,0 +1,20 @@ +--- +"@maxmorozoff/try-catch-tuple": patch +--- + +docs: + +- Add JSDoc for TryCatch types + +chore: + +- Export missing types +- Update default error type +- Update Branded type +- Update DataErrorTuple type (e.g., `rest` is now `never[]`) +- Restrict array methods on DataErrorTuple +- Make `fn` parameter required + +refactor: + +- Extract DataErrorTuple to type alias