{"id":28528622,"url":"https://github.com/maxmorozoff/try-catch-tuple","last_synced_at":"2025-08-10T08:04:03.159Z","repository":{"id":283752446,"uuid":"952787613","full_name":"maxmorozoff/try-catch-tuple","owner":"maxmorozoff","description":"A TypeScript utility for Go-style structured error handling (`[data, error]`) combined with powerful tooling (TypeScript Plugin \u0026 Build Transformer) to ensure correctness.","archived":false,"fork":false,"pushed_at":"2025-07-25T11:02:18.000Z","size":156,"stargazers_count":19,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-08T19:27:44.606Z","etag":null,"topics":["try-catch","try-catch-js","try-catch-wrapper","ts-plugin","ts-utils"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maxmorozoff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-03-21T22:09:46.000Z","updated_at":"2025-07-25T11:02:22.000Z","dependencies_parsed_at":"2025-06-09T13:10:18.495Z","dependency_job_id":"70ac115a-d7d3-4806-b3fb-d476ccad3cc5","html_url":"https://github.com/maxmorozoff/try-catch-tuple","commit_stats":null,"previous_names":["maxmorozoff/try-catch-tuple"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/maxmorozoff/try-catch-tuple","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxmorozoff%2Ftry-catch-tuple","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxmorozoff%2Ftry-catch-tuple/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxmorozoff%2Ftry-catch-tuple/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxmorozoff%2Ftry-catch-tuple/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxmorozoff","download_url":"https://codeload.github.com/maxmorozoff/try-catch-tuple/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxmorozoff%2Ftry-catch-tuple/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269649368,"owners_count":24453502,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-09T02:00:10.424Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["try-catch","try-catch-js","try-catch-wrapper","ts-plugin","ts-utils"],"created_at":"2025-06-09T13:10:13.137Z","updated_at":"2025-08-10T08:04:03.144Z","avatar_url":"https://github.com/maxmorozoff.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tryCatch Utility \u0026 Validation Tools\n\n[![CI](https://github.com/maxmorozoff/try-catch-tuple/actions/workflows/ci.yml/badge.svg)](https://github.com/maxmorozoff/try-catch-tuple/actions/workflows/ci.yml)\n[![Release](https://github.com/maxmorozoff/try-catch-tuple/actions/workflows/release.yml/badge.svg)](https://github.com/maxmorozoff/try-catch-tuple/actions/workflows/release.yml)\n[![try-catch NPM Version](https://img.shields.io/npm/v/%40maxmorozoff%2Ftry-catch-tuple?label=tryCatch)](https://www.npmjs.com/package/@maxmorozoff/try-catch-tuple)\n[![ts-plugin NPM Version](https://img.shields.io/npm/v/%40maxmorozoff%2Ftry-catch-tuple-ts-plugin?label=ts-plugin)](https://www.npmjs.com/package/@maxmorozoff/try-catch-tuple-ts-plugin)\n[![License: MIT](https://img.shields.io/badge/License-MIT-gold.svg)](https://github.com/maxmorozoff/try-catch-tuple/LICENSE)\n\nA TypeScript utility for Go-style structured error handling (`[data, error]`) combined with powerful tooling (TypeScript Plugin \u0026 Build Transformer) to ensure correctness.\n\n## Overview\n\nThis repository provides two key components designed to work together:\n\n1. **[`try-catch-tuple`](#trycatch-utility-maxmorozofftry-catch-tuple):** A utility function for wrapping synchronous or asynchronous operations, returning a tuple `[data, error]` inspired by Go's error handling pattern (error last).\n2. **[`try-catch-tuple-ts-plugin`](#plugin--transformer-maxmorozofftry-catch-tuple-ts-plugin):** Tooling (Language Service Plugin + Build Transformer) that integrates with TypeScript to enforce correct destructuring and handling of the `[data, error]` tuple returned by the utility.\n\n\u003e [!IMPORTANT]  \n\u003e While `try-catch-tuple` is considered production-ready due to its minimal implementation approach,  \n\u003e `try-catch-tuple-ts-plugin` is a proof of concept (PoC) and still in its early stages.\n\n## Showcase: Plugin \u0026 Code Fixes\n\n_(See the TypeScript plugin in action, catching errors and providing fixes in the editor)_\n\nhttps://gist.github.com/user-attachments/assets/1c00381f-985a-4484-b2a1-e87665877fb4\n\n## Rationale: Error Last \u0026 Tooling\n\nTraditional Node.js error handling often uses callbacks with `(error, data)`. Many modern utilities and proposals ([like this one](https://github.com/arthurfiorette/proposal-try-operator)) also adopt an \"error first\" tuple `[error, data]`.\n\nThis library takes a different approach, placing the **error last (`[data, error]`)**, similar to Go.\n\n**Why `error` last?**\n\n- **Scanning Intent:** When fetching or processing data, the primary goal is often the `data`. Placing it first aligns the code structure with the primary intent, potentially making success paths easier to visually scan. Code often reads like \"get the data, then check for an error\".\n- **Intuition:** For developers familiar with Go or similar paradigms, this can feel more natural.\n\n**The Challenge: Explicit Error Handling**\n\nA potential downside of the error-last pattern is the risk of accidentally forgetting to check the `error` value. As discussed in community ([like this issue](https://github.com/arthurfiorette/proposal-try-operator/issues/13) or [this gist comment](https://gist.github.com/t3dotgg/a486c4ae66d32bf17c09c73609dacc5b?permalink_comment_id=5511839#gistcomment-5511839)), error handling should ideally be explicit. Swallowing errors silently is dangerous.\n\n**The Solution: Tooling Enforcement**\n\nThis repository strongly advocates for using the provided **TypeScript tooling** alongside the `tryCatch` utility. The Language Service Plugin and Build Transformer act as a safety net:\n\n- They **enforce** that the returned tuple is destructured correctly (`[data, error]` or `[data, ,]`).\n- They prevent accidentally ignoring the error (e.g., `const [data] = tryCatch(...)` or `const result = tryCatch(...)`).\n- This allows developers to benefit from the potential readability of the error-last pattern while **mitigating the risk** of unhandled errors through compile-time and IDE checks.\n\nEssentially, we leverage TypeScript's powerful type system and tooling capabilities to make the error-last pattern safe and explicit.\n\n## Why a Tuple (`[data, error]`) Return Type?\n\nWhile other libraries or patterns might return an object like `{ data: T, error: E }`, this utility deliberately uses a tuple `[data, error]`. This decision is intertwined with the \"Error Last\" rationale and the emphasis on tooling:\n\n1. **Explicit Handling Encouraged:** With an object `{ data, error }`, it's syntactically very easy to ignore the error property simply by omitting it during destructuring:\n\n   ```typescript\n   // Easy to forget the error without tooling\n   const { data } = tryCatchReturningObject(...); // 'error' is implicitly ignored\n   ```\n\n   While convenient, this increases the risk of accidentally swallowing errors if the developer forgets to handle the `error` case separately. The tuple structure `[data, error]` forces the developer to acknowledge both positions during destructuring.\n\n2. **Cleaner Renaming (Especially for Data):** Renaming during destructuring is arguably more straightforward for the primary `data` value with tuples:\n\n   ```typescript\n   // Tuple Renaming\n   const [user, userError] = tryCatch(...); // 'user' directly gets the data\n\n   // Object Renaming\n   const { data: user, error: userError } = tryCatchReturningObject(...); // Requires explicit 'data:' label\n   ```\n\n   While minor, it keeps the focus on the primary success value when renaming.\n\n3. **Tooling Makes Tuples Safe:** The potential drawback of tuples (like forgetting which index is which, although named tuples mitigate this) is less significant when paired with the TypeScript plugin/transformer. The tooling enforces that _both_ elements are acknowledged (either `[data, error]` or `[data, ,]` if allowed), effectively preventing the accidental ignoring of the `error` element, which was the main safety concern with the tuple pattern.\n\n4. **Future Considerations (Object/Combined Approach):** We recognize the ergonomic benefits an object-based or combined approach (like [czy.js](https://github.com/osoclos/czy-js)) can offer. While the current focus is on the tuple pattern enforced by tooling, **we may explore supporting an object-based return type as a configurable option in the future.** Contributions towards this are welcome! The goal would be to ensure any approach maintains explicit error handling, potentially through enhanced tooling checks specific to the object pattern.\n\n## Why a TypeScript Plugin/Transformer (vs. ESLint)?\n\nWhile ESLint is a powerful and widely-used linting tool, we chose to implement this validation logic directly within the TypeScript ecosystem (as a Language Service Plugin and a Build Transformer) for several key reasons:\n\n1. **Deep Type System Integration:** The core requirement of validating wrapped function calls (`checkWrappedCalls: true`) necessitates understanding the _return types_ of functions. This requires deep integration with TypeScript's Type Checker, which is readily available within TS Plugins and Transformers but often more complex or less performant to achieve accurately within ESLint rules (which typically operate more on the AST structure).\n2. **Build Process Integration (`tsc`):** The build transformer integrates directly into the `tsc` compilation process via `ts-patch`. This ensures that validation failures (when configured as errors) block the build itself, providing a strong guarantee of correctness before code is shipped. While ESLint can be part of a build script, it runs as a separate step.\n3. **Real-time IDE Feedback:** Language Service Plugins offer the tightest integration with editors like VS Code, providing instant feedback, squiggles, and code fixes as you type. Achieving the same level of responsiveness and type-aware code fixes with ESLint can be more challenging.\n4. **Evolving Linting Landscape:** While ESLint remains dominant, the ecosystem for linting and formatting JavaScript/TypeScript is evolving, with tools like Biome gaining traction. Focusing on TypeScript's own extension points provides a robust solution tied directly to the language itself.\n\n**ESLint Rule Possibility:**\n\nThat being said, an ESLint rule _could_ potentially be developed to cover at least the _basic_ destructuring validation (checking `const [a,b] = tryCatch(...)` vs `const result = tryCatch(...)`). Implementing the type-checking required for wrapped calls would be the main challenge.\n\n**We welcome contributions!** If you're interested in developing and maintaining an ESLint plugin for this utility, please feel free to open an issue or pull request to discuss it.\n\n## `tryCatch` Utility (`@maxmorozoff/try-catch-tuple`)\n\n### Features\n\n- Handles both synchronous and asynchronous functions/promises.\n- Returns a structured, branded tuple `[data, error]`.\n- Provides named operations (`tryCatch(fn, \"Operation Name\")`) for better debugging context in errors.\n- Includes `tryCatch.sync` and `tryCatch.async` for explicit handling.\n- Allows custom error types via `.errors\u003cE\u003e()`.\n- Ensures all thrown values are normalized into `Error` instances.\n\n### Basic Usage\n\n```typescript\nimport { tryCatch } from \"@maxmorozoff/try-catch-tuple\";\n\n// Synchronous\nfunction parseJson(str: string) {\n  const [result, error] = tryCatch(() =\u003e JSON.parse(str) as { id: number });\n  //     ^? const result: { id: number } | null\n\n  if (error) {\n    // Always check the error!\n    console.error(\"Parsing failed:\", error); // `error` is an `Error` instance\n    //                               ^? const error: Error\n    return null;\n  }\n\n  // Type refinement works here\n  return result; // ✅ result: { id: number }\n}\n\n// Asynchronous\nasync function fetchUser(id: number): Promise\u003cUser\u003e {\n  // ... fetch logic\n  if (id \u003c 0) throw new Error(\"Invalid ID\");\n  return { name: \"Alice\" };\n}\n\nasync function getUser(id: number) {\n  const [user, error] = await tryCatch(fetchUser(id));\n  //     ^? const user: User | null\n\n  if (error) {\n    console.error(`Failed to get user ${id}:`, error.message);\n    return null;\n  }\n\n  return user; // ✅ user: User\n}\n```\n\n_(See more advanced `tryCatch` usage examples and API reference further down.)_\n\n## Plugin \u0026 Transformer (`@maxmorozoff/try-catch-tuple-ts-plugin`)\n\n### Plugin Features\n\n- **Strict Destructuring:** Enforces `[data, error]` or `[data, ,]` (if configured).\n- **Configurable Error Ignoring:** `allowIgnoredError` option (defaults to `true`) permits `[data, ,]`.\n- **Direct \u0026 Await Call Validation:** Catches errors in both sync and async contexts.\n- **Wrapped Call Detection:** Uses Type Checker + branding to validate results from wrapper functions (`checkWrappedCalls`, default `true`).\n- **IDE Integration:** Real-time errors/warnings and Quick Fixes in editors like VS Code.\n- **Build-Time Checks:** Reports errors/warnings during `tsc` compilation via `ts-patch`.\n- **Configurable Severity:** Report as `\"error\"` or `\"warning\"`.\n\n## Installation\n\n```bash\n# 1. Install the utility (prod dependency):\nnpm i @maxmorozoff/try-catch-tuple \n\n# 2. Install ts plugin (dev dependencies):\n\n# If using the build transformer, ts-patch is also required\nnpm i -D @maxmorozoff/try-catch-tuple-ts-plugin ts-patch typescript\n\n# Or for utility + LSP only:\nnpm i -D @maxmorozoff/try-catch-tuple-ts-plugin typescript\n```\n\n## Configuration (`tsconfig.json`)\n\nConfigure the tooling under `compilerOptions.plugins`.\n\n**1. Both LSP Plugin and Build Transformer (Recommended Combined Approach - Requires `ts-patch`):**\n\n```jsonc\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    // ... your other options\n    \"plugins\": [\n      {\n        \"name\": \"@maxmorozoff/try-catch-tuple-ts-plugin\", // For LSP\n        \"transform\": \"@maxmorozoff/try-catch-tuple-ts-plugin/transformer\", // For Build\n        // --- SHARED Configuration (applies to both) ---\n        \"errorLevel\": \"error\", // Default: \"error\"\n        \"allowIgnoredError\": true, // Default: true\n        \"checkWrappedCalls\": true // Default: true\n      }\n    ]\n  }\n  // ...\n}\n```\n\n**2. LSP Plugin Only:**\n\n```jsonc\n// tsconfig.json\n{\n  \"compilerOptions\": {\n    // ... your other options\n    \"plugins\": [\n      {\n        \"name\": \"@maxmorozoff/try-catch-tuple-ts-plugin\",\n        // --- Optional Configuration for LSP ---\n        \"errorLevel\": \"error\",\n        \"allowIgnoredError\": true,\n        \"checkWrappedCalls\": true\n      }\n    ]\n  }\n  // ...\n}\n```\n\n## Usage\n\n### 1. IDE (Language Service Plugin)\n\n- **Select Workspace TypeScript Version:** Ensure your editor is using the workspace's TypeScript version instead of the built-in one (e.g., VS Code: `TypeScript: Select TypeScript Version`).\n- **Restart TS Server:** After configuring the plugin, **restart the TypeScript Server** (e.g., VS Code: `TypeScript: Restart TS server`).\n- Errors will be underlined, and Quick Fixes will be available.\n\n### 2. Build (Transformer)\n\n#### Method 1: Live Compiler\n\nThe live compiler patches on-the-fly, each time it is run.\n\n**Via commandline:** Simply use `tspc` (instead of `tsc`)\n\n**With tools such as ts-node, webpack, ts-jest, etc:** specify the compiler as  `ts-patch/compiler`\n\n#### Method 2: Persistent Patch\n\nPersistent patch modifies the typescript installation within the `node_modules` path. It requires additional configuration\nto remain persisted, but it carries less load time and complexity compared to the live compiler.\n\n1. Install the patch\n\n```shell\n# For advanced options, see: ts-patch /?\nts-patch install\n```\n\n2. Add `prepare` script (keeps patch persisted after npm install)\n\n`package.json`\n```jsonc\n{\n  /* ... */\n  \"scripts\": {\n    \"prepare\": \"ts-patch install -s\"\n  }\n}\n```\n\nFor advanced options, see: [ts-patch docs](https://github.com/nonara/ts-patch?tab=readme-ov-file#usage)\n\n## Configuration Options (Plugin \u0026 Transformer)\n\n| Option              | Type                     | Default   | Description                                                                                                                                 |\n| :------------------ | :----------------------- | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------ |\n| `errorLevel`        | `\"error\"` \\| `\"warning\"` | `\"error\"` | Sets the severity level. For the transformer, `\"error\"` reports as a `tsc` error, potentially failing the build.                            |\n| `allowIgnoredError` | `boolean`                | `true`    | If `true`, allows destructuring as `[data, ,]` to explicitly ignore the error element.                                                      |\n| `checkWrappedCalls` | `boolean`                | `true`    | If `true`, uses the Type Checker to analyze calls to functions other than `tryCatch` to see if they return the expected branded tuple type. |\n\n## Expected `tryCatch` Result Type (Branded Tuple)\n\nThe tooling relies on the `tryCatch` function returning a **branded tuple union** structure:\n\n```typescript\ninterface TryCatchBrand {\n  __tryCatchTupleResult: \"marker\"; // The unique brand property\n}\ntype Success\u003cT\u003e = TryCatchBrand \u0026 [data: T, error: null];\ntype Failure\u003cE\u003e = TryCatchBrand \u0026 [data: null, error: E | Error];\nexport type Result\u003cT, E = Error\u003e = Success\u003cT\u003e | Failure\u003cE\u003e;\n```\n\nThe presence of `__tryCatchTupleResult` is essential for the `checkWrappedCalls` feature.\n\n## Validation Rules (Enforced by Tooling)\n\n**Valid Usage:**\n\n```typescript\n// Standard\nconst [data, error] = tryCatch(...);\nconst [data, err] = await tryCatch(...);\n\n// Using underscore for error\nconst [data, _] = tryCatch(...);\n\n// Explicitly ignoring error (Valid by default because allowIgnoredError: true)\nconst [data, ,] = tryCatch(...);\n\n// Wrapped calls (if checkWrappedCalls: true and type matches)\nconst wrapped = () =\u003e tryCatch(...);\nconst [d, e] = wrapped();\n```\n\n**Invalid Usage:**\n\n```typescript\n// Not destructured -\u003e Error\nconst result = tryCatch(...);\n\n// Missing elements -\u003e Error (expects 2 positions)\nconst [data] = tryCatch(...);\nconst [] = tryCatch(...);\n\n// Too many elements -\u003e Error\nconst [data, error, extra] = tryCatch(...);\n\n// Wrapped calls with incorrect destructuring -\u003e Error (if checkWrappedCalls: true)\nconst wrapped = () =\u003e tryCatch(...);\nconst [d] = wrapped();\nconst res = await wrapped();\n```\n\n## Code Fixes (Language Service Plugin)\n\nWhen an invalid pattern is detected:\n\n1. **`Destructure return as [result, error]`**: Fixes to the standard pattern.\n2. **`Destructure return as [result, ,] (ignore error)`**: (Only if `allowIgnoredError: true`) Fixes to the ignored error pattern.\n\n---\n\n## More `tryCatch` Examples \u0026 API\n\n### Named Operations for Debugging\n\n```ts\nconst [result, error] = tryCatch((): void =\u003e {\n  throw new Error(\"Failed to fetch data\");\n}, \"Fetch Data\");\n\n// error?.message -\u003e \"Operation \\\"Fetch Data\\\" failed: Failed to fetch data\"\n```\n\n### Explicit Sync/Async\n\n```ts\nconst [resSync, errSync] = tryCatch.sync(() =\u003e /* sync op */);\nconst [resAsync, errAsync] = await tryCatch.async(async () =\u003e /* async op */);\n```\n\n### Handling \u0026 Customizing Errors\n\nIf a non-Error is thrown, it's wrapped:\n\n```ts\nconst [, error] = tryCatch(() =\u003e {\n  throw \"Oops\";\n});\n// error is instance of Error, error.message is \"Oops\"\n```\n\nSpecify expected error types:\n\n```ts\ntype UserError = SyntaxError | NetworkError;\n\n// Option 1: Manual types\nconst [user, error] = await tryCatch\u003cPromise\u003cUser\u003e, UserError\u003e(fetchUser(1));\n// error type: UserError | Error | null\n// user type: User | null\n\n// Option 2: .errors\u003cE\u003e() helper\nconst [user, error] = await tryCatch.errors\u003cUserError\u003e()(fetchUser(1));\n// error type: UserError | Error | null\n// user type: User (inferred from fetchUser) | null\n```\n\n### Wrapping Functions\n\n```ts\nconst getUser = (id: number) =\u003e\n  tryCatch\n    .errors\u003cRangeError | SyntaxError\u003e() // Chain errors\n    .async(fetchUser(id)); // Use async helper if needed\n\nasync function main() {\n  const [user, error] = await getUser(1);\n  if (error) {\n    // error type includes RangeError, SyntaxError, and base Error\n    /* ... */\n  }\n}\n```\n\n### React Server Components (RSC) Example\n\n```tsx\nconst getUser = (id: number) =\u003e tryCatch.errors\u003cSpecificError\u003e()(fetchUser(id));\n\nasync function UserPage({ id }: { id: number }) {\n  const [user, error] = await getUser(id);\n\n  if (error) {\n    // Handle specific errors or show generic message\n    if (error instanceof SpecificError)\n      return \u003cdiv\u003eSpecific error occurred\u003c/div\u003e;\n    return \u003cdiv\u003eUser not found or error occurred.\u003c/div\u003e;\n  }\n\n  return \u003cdiv\u003eHello {user.name}!\u003c/div\u003e;\n}\n```\n\n### Comparison with `try...catch`\n\n```ts\nasync function goodFunc() {\n  if (false) throw \"no data\";\n  return \"some data\";\n}\n\nasync function badFunc() {\n  if (true) throw \"no data\";\n  return \"some data\";\n}\n\n// ✅ Using tryCatch\nconst getData = async () =\u003e {\n  let [data, err] = await tryCatch(badFunc);\n  if (!err) return Response.json({ data });\n\n  [data, err] = await tryCatch(badFunc);\n  if (!err) return Response.json({ data });\n\n  [data, err] = await tryCatch(goodFunc);\n  if (!err) return Response.json({ data });\n\n  return Response.error();\n};\n\n// ✅ Using tryCatch with constants\nconst getDataConst = async () =\u003e {\n  const [data1, err1] = await tryCatch(badFunc);\n  if (!err1) return Response.json({ data: data1 });\n\n  const [data2, err2] = await tryCatch(badFunc);\n  if (!err2) return Response.json({ data: data2 });\n\n  const [data3, err3] = await tryCatch(goodFunc);\n  if (!err3) return Response.json({ data: data3 });\n\n  return Response.error();\n};\n\n// ❌ Using traditional try...catch (deep nesting)\nconst getDataStandard = async () =\u003e {\n  try {\n    const data = await badFunc();\n    return Response.json({ data });\n  } catch (err) {\n    try {\n      const data = await badFunc();\n      return Response.json({ data });\n    } catch (err) {\n      try {\n        const data = await goodFunc();\n        return Response.json({ data });\n      } catch (err) {\n        return Response.error();\n      }\n    }\n  }\n};\n```\n\n### API Reference\n\n#### Main Function\n\n```ts\ntryCatch\u003cT, E extends Error = never\u003e(fn?: (() =\u003e T) | T | Promise\u003cT\u003e | (() =\u003e Promise\u003cT\u003e), operationName?: string): Result\u003cT, E\u003e\n```\n\n- Handles values, sync/async functions\n- Automatically detects Promises\n\n#### Explicit Synchronous Handling\n\n```ts\ntryCatch.sync\u003cT, E extends Error = never\u003e(fn: () =\u003e T, operationName?: string): Result\u003cT, E\u003e\n```\n\n#### Explicit Asynchronous Handling\n\n```ts\ntryCatch.async\u003cT, E extends Error = never\u003e(fn: Promise\u003cT\u003e | (() =\u003e Promise\u003cT\u003e), operationName?: string): Promise\u003cResult\u003cT, E\u003e\u003e\n```\n\n#### Result Type\n\n```ts\ntype Result\u003cT, E = Error\u003e = ([data: T, error: null] | [data: null, error: E]) \u0026\n  TryCatchBrand;\n```\n\n### Edge Cases\n\n```ts\ntryCatch(undefined); // Returns [undefined, null]\ntryCatch(null); // Returns [null, null]\ntryCatch(() =\u003e {\n  throw new Error(\"Unexpected Error\");\n}); // Returns [null, Error]\ntryCatch(() =\u003e {\n  throw null;\n}); // Returns [null, Error]\ntryCatch(Promise.reject(new Error(\"Promise rejected\"))); // Handles rejected promises\n```\n\n## Development\n\n```bash\n# Install dependencies\nbun install\n\n# Build packages\nbun run build\n\n# Run tests\nbun run test\n```\n\n## Contributions\n\nContributions of any kind are much appreciated.\n\n\u003c!-- Please refer to the contribution guidelines (TBD). --\u003e\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxmorozoff%2Ftry-catch-tuple","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxmorozoff%2Ftry-catch-tuple","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxmorozoff%2Ftry-catch-tuple/lists"}