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 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 26e8f0e..83437c3 100644 --- a/packages/try-catch-tuple/src/tryCatch.ts +++ b/packages/try-catch-tuple/src/tryCatch.ts @@ -1,29 +1,91 @@ -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 Branded = T & { __tryCatchTupleResult: never }; +type DisableArrayMethods = T & { + [K in Exclude, "length" | symbol>]: never; +}; + +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; -type TryCatchResult = T extends Promise +/** + * 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; -type TryCatchFunc = ( - fn?: T | (() => T), +/** + * 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; -type TryCatch< +/** + * 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 = never, + 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_>; }; @@ -31,12 +93,12 @@ 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 = ( - fn?: T | (() => T), +export const tryCatch: TryCatch = ( + fn: T | (() => T), operationName?: string, ) => { try { @@ -51,7 +113,7 @@ export const tryCatch: TryCatch = ( } }; -export const tryCatchSync: TryCatch["sync"] = ( +export const tryCatchSync: TryCatch["sync"] = ( fn: () => T, operationName?: string, ) => { @@ -65,7 +127,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, 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(); });