Skip to content

Usage Guide

This guide expands on the rationale and gives actionable patterns for using @maxmorozoff/try-catch-tuple in real projects. It focuses on:

  • Establishing consistency in how you wrap operations.
  • Designing thin, intention-revealing wrappers.
  • Keeping error handling explicit and localized.
  • Leveraging the TypeScript Plugin / Transformer to enforce correctness.
  • Building domain-specific helpers on top of the primitive [data, error] pattern.

  1. Treat [data, error] as a branching point—handle immediately or explicitly propagate.
  2. Wrap side-effectful or failure-prone boundaries: I/O, parsing, external services.
  3. Give operations names (operationName) when they cross logical or infrastructural boundaries—this enriches error context.

Every potentially failing call looks structurally the same:

const [result,
const error: Error | null
error
] = await
tryCatch<Promise<string>, Error>(fn: Promise<string> | (() => Promise<string>), operationName?: string): Promise<Result<string, Error>>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
(
function possiblyFailingWork(): Promise<string>
possiblyFailingWork
, "Human Friendly Operation");
const result: string | null
if (
const error: Error | null
error
) {
// Handle, map, log, rethrow, or return upstream
result; // Note the type
const result: null
return
const error: Error
error
.
Error.message: string
message
;
}
// Continue with refined `result`
result;
const result: string

Because this repeats often, move the risky part behind a small wrapper:

async function
function fetchUser(userId: string): Promise<Result<User, Error>>
fetchUser
(
userId: string
userId
: string) {
return
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <User, Error>(fn: Promise<User> | (() => Promise<User>), operationName?: string) => Promise<Result<User, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(
() =>
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+3 overloads)

Send a HTTP(s) request

@paramrequest Request object

@paraminit A structured value that contains settings for the fetch() request.

@returnsA promise that resolves to Response object.

fetch
(`https://api.example.com/users/${
userId: string
userId
}`).
Promise<Response>.then<User, never>(onfulfilled?: ((value: Response) => User | PromiseLike<User>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.

@paramonrejected The callback to execute when the Promise is rejected.

@returnsA Promise for the completion of which ever callback is executed.

then
(
r: Response
r
=> {
if (!
r: Response
r
.
Response.ok: boolean
ok
) throw new
var Error: ErrorConstructor
new (message?: string) => Error
Error
(`HTTP ${
r: Response
r
.
Response.status: number
status
}`);
return
r: Response
r
.
BodyMixin.json: () => Promise<unknown>
json
() as
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface User
User
>;
}),
"Fetch User"
);
}
// Usage
const [ user,
const userErr: Error | null
userErr
] = await
function fetchUser(userId: string): Promise<Result<User, Error>>
fetchUser
("123");
const user: User | null
if (
const userErr: Error | null
userErr
) {
/* ... */
return;
}
// Continue with refined `user`
user;
const user: User

Wrap an operation if ANY of these apply:

ConditionWrap?Reason
try...catch is usedYesEliminates try..catch usage and deeply nested code.
External boundary (network, FS, DB)YesStabilizes error shape / context.
Complex parsing / transformationYesIsolates failure + naming improves debuggability.
Single, trivial computation, no throwNoJust return the value.
Hot path performance concernMaybeMeasure first; overhead is minimal but still a function boundary.

Pick contextual, action-based phrases:

  • “Fetch User Profile”
  • “Parse Settings JSON”
  • “Write Cache File”
  • “Open DB Transaction”
  • “Render Markdown”

Avoid over‑specific internal details (e.g., “fs.readFileSync user.json” → prefer “Load User Cache File”).


Use .errors<E>() to express expected domain or infra error classes:

class
class NetworkError
NetworkError
extends
var Error: ErrorConstructor
Error
{}
class
class DecodeError
DecodeError
extends
var Error: ErrorConstructor
Error
{}
class
class NotFoundError
NotFoundError
extends
var Error: ErrorConstructor
Error
{}
const
const getPost: (id: string) => Promise<Result<Post, NetworkError | DecodeError | NotFoundError | Error>>
getPost
= (
id: string
id
: string) =>
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
errors: <NetworkError | DecodeError | NotFoundError>() => TryCatch<TryCatchFunc<NetworkError | DecodeError | NotFoundError | Error>, NetworkError | ... 2 more ... | Error>

Creates a new TryCatch instance that handles additional error types.

@returnsA new TryCatch instance with extended error handling capabilities.

errors
<
class NetworkError
NetworkError
|
class DecodeError
DecodeError
|
class NotFoundError
NotFoundError
>()
.
async: <Post, NetworkError | DecodeError | NotFoundError | Error>(fn: Promise<Post> | (() => Promise<Post>), operationName?: string) => Promise<...>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async () => {
const
const res: Response
res
= await
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+3 overloads)

Send a HTTP(s) request

@paramrequest Request object

@paraminit A structured value that contains settings for the fetch() request.

@returnsA promise that resolves to Response object.

fetch
(`/posts/${
id: string
id
}`);
if (
const res: Response
res
.
Response.status: number
status
=== 404) throw new
constructor NotFoundError(message?: string): NotFoundError
NotFoundError
("Post not found");
if (!
const res: Response
res
.
Response.ok: boolean
ok
) throw new
constructor NetworkError(message?: string): NetworkError
NetworkError
(`Status ${
const res: Response
res
.
Response.status: number
status
}`);
try {
return (await
const res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
()) as
interface Post
Post
;
} catch {
throw new
constructor DecodeError(message?: string): DecodeError
DecodeError
("Invalid post JSON");
}
}, "Fetch Post");

Downstream:

const [
const post: Post | null
post
,
const postErr: Error | NetworkError | DecodeError | NotFoundError | null
postErr
] = await
const getPost: (id: string) => Promise<Result<Post, Error | NetworkError | DecodeError | NotFoundError>>
getPost
("42");
if (
const postErr: Error | NetworkError | DecodeError | NotFoundError | null
postErr
) {
if (
const postErr: Error | NetworkError | DecodeError | NotFoundError
postErr
instanceof
class NetworkError
NetworkError
)
return
function handleNetwork(error: NetworkError): void
handleNetwork
(
const postErr: NetworkError
postErr
);
if (
const postErr: Error | DecodeError | NotFoundError
postErr
instanceof
class DecodeError
DecodeError
)
return
function handleCorrupt(error: DecodeError): void
handleCorrupt
(
const postErr: DecodeError
postErr
);
if (
const postErr: Error | NotFoundError
postErr
instanceof
class NotFoundError
NotFoundError
)
return
function handleMissing(error: NotFoundError): void
handleMissing
(
const postErr: NotFoundError
postErr
);
// fallback generic
return
function handleGeneric(error: Error): void
handleGeneric
(
const postErr: Error
postErr
);
}

Create a utils/ or lib/ module that centralizes patterns.

./src/lib/json.ts
import {
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
} from "@maxmorozoff/try-catch-tuple";
export const
const parseJson: <T = unknown>(raw: string, operationName?: string) => TryCatchResult<T, Error>
parseJson
= <
function (type parameter) T in <T = unknown>(raw: string, operationName?: string): TryCatchResult<T, Error>
T
= unknown>(
raw: string
raw
: string,
operationName: string
operationName
= "Parse JSON") =>
tryCatch<T, Error>(fn: T | (() => T), operationName?: string): TryCatchResult<T, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
(() =>
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.parse(text: string, reviver?: (this: any, key: string, value: any) => any): any

Converts a JavaScript Object Notation (JSON) string into an object.

@paramtext A valid JSON string.

@paramreviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is.

parse
(
raw: string
raw
) as
function (type parameter) T in <T = unknown>(raw: string, operationName?: string): TryCatchResult<T, Error>
T
,
operationName: string
operationName
);

Usage:

const [
const user: User | null
user
,
const parseErr: Error | null
parseErr
] =
const parseJson: <User>(raw: string, operationName?: string) => Result<User, Error>
parseJson
<
interface User
User
>(
any
rawResponse
, "Parse User JSON");
if (!
const parseErr: Error | null
parseErr
) return
const user: User
user
;
./src/lib/fetch.ts
import {
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
} from "@maxmorozoff/try-catch-tuple";
export interface
interface SafeFetchOptions
SafeFetchOptions
extends
interface RequestInit
RequestInit
{
SafeFetchOptions.expectJson?: boolean
expectJson
?: boolean;
SafeFetchOptions.operationName?: string
operationName
?: string;
}
export function
function safeFetch<T = unknown>(input: RequestInfo | URL, { expectJson, operationName, ...init }?: SafeFetchOptions): Promise<Result<T, Error>>
safeFetch
<
function (type parameter) T in safeFetch<T = unknown>(input: RequestInfo | URL, { expectJson, operationName, ...init }?: SafeFetchOptions): Promise<Result<T, Error>>
T
= unknown>(
input: any
input
:
type RequestInfo = /*unresolved*/ any
RequestInfo
|
interface URL

URL class is a global reference for import { URL } from 'url' https://nodejs.org/api/url.html#the-whatwg-url-api

@sincev10.0.0

URL
,
{
expectJson: boolean
expectJson
= true,
operationName: string
operationName
= "Fetch", ...
init: {
method?: string;
keepalive?: boolean;
redirect?: RequestRedirect;
integrity?: string;
signal?: AbortSignal | null;
credentials?: RequestCredentials;
... 7 more ...;
headers?: Bun.HeadersInit;
}
init
}:
interface SafeFetchOptions
SafeFetchOptions
= {}
) {
return
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <T, Error>(fn: Promise<T> | (() => Promise<T>), operationName?: string) => Promise<Result<T, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async () => {
const
const res: Response
res
= await
function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+3 overloads)

Send a HTTP(s) request

@paramrequest Request object

@paraminit A structured value that contains settings for the fetch() request.

@returnsA promise that resolves to Response object.

fetch
(
input: any
input
,
init: {
method?: string;
keepalive?: boolean;
redirect?: RequestRedirect;
integrity?: string;
signal?: AbortSignal | null;
credentials?: RequestCredentials;
... 7 more ...;
headers?: Bun.HeadersInit;
}
init
);
if (!
const res: Response
res
.
Response.ok: boolean
ok
) {
// Optionally collect headers / body snippet
throw new
var Error: ErrorConstructor
new (message?: string) => Error
Error
(`HTTP ${
const res: Response
res
.
Response.status: number
status
} for ${
operationName: string
operationName
}`);
}
if (!
expectJson: boolean
expectJson
) return
const res: Response
res
as unknown as
function (type parameter) T in safeFetch<T = unknown>(input: RequestInfo | URL, { expectJson, operationName, ...init }?: SafeFetchOptions): Promise<Result<T, Error>>
T
;
return (await
const res: Response
res
.
BodyMixin.json: () => Promise<unknown>
json
()) as
function (type parameter) T in safeFetch<T = unknown>(input: RequestInfo | URL, { expectJson, operationName, ...init }?: SafeFetchOptions): Promise<Result<T, Error>>
T
;
},
operationName: string
operationName
);
}

Usage:

const [
const user: User | null
user
,
const userErr: Error | null
userErr
] = await
function safeFetch<User>(input: RequestInfo | URL, { expectJson, operationName, ...init }?: SafeFetchOptions): Promise<Result<User, Error>>
safeFetch
<
interface User
User
>(`/api/users/${
const id: string
id
}`, {
SafeFetchOptions.operationName?: string
operationName
: "Fetch User",
});
if (!
const userErr: Error | null
userErr
) {
// Use user
return
const user: User
user
;
} else {
// Handle error
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.error(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stderr with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const code = 5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr

If formatting elements (e.g. %d) are not found in the first string then util.inspect() is called on each argument and the resulting string values are concatenated. See util.format() for more information.

@sincev0.1.100

error
(
const userErr: Error
userErr
);
}
./src/lib/fs.ts
import {
module "node:fs/promises"
export promises
promises
as
module "node:fs/promises"
fs
} from "node:fs";
import {
function (method) dirname(path: string): string

Return the directory name of a path. Similar to the Unix dirname command.

@parampath the path to evaluate.

@throws{TypeError} if path is not a string.

dirname
} from "node:path";
import {
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
} from "@maxmorozoff/try-catch-tuple";
export const
const readFileSafe: (path: string, encoding?: BufferEncoding) => Promise<Result<string, Error>>
readFileSafe
= (
path: string
path
: string,
encoding: BufferEncoding
encoding
:
type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "utf-16le" | "ucs2" | "ucs-2" | "base64" | "base64url" | "latin1" | "binary" | "hex"
BufferEncoding
= "utf8") =>
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <string, Error>(fn: Promise<string> | (() => Promise<string>), operationName?: string) => Promise<Result<string, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(() =>
module "node:fs/promises"
fs
.
function readFile(path: PathLike | fs.FileHandle, options: ({
encoding: BufferEncoding;
flag?: OpenMode | undefined;
} & EventEmitter<T extends EventMap<T> = DefaultEventMap>.Abortable) | BufferEncoding): Promise<string> (+2 overloads)

Asynchronously reads the entire contents of a file.

@parampath A path to a file. If a URL is provided, it must use the file: protocol. If a FileHandle is provided, the underlying file will not be closed automatically.

@paramoptions An object that may contain an optional flag. If a flag is not provided, it defaults to 'r'.

readFile
(
path: string
path
, {
encoding: BufferEncoding
encoding
}), `Read File: ${
path: string
path
}`);
export const
const writeFileSafe: (path: string, data: string | Buffer) => Promise<Result<boolean, Error>>
writeFileSafe
= (
path: string
path
: string,
data: string | Buffer<ArrayBufferLike>
data
: string |
interface Buffer<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
Buffer
) =>
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <boolean, Error>(fn: Promise<boolean> | (() => Promise<boolean>), operationName?: string) => Promise<Result<boolean, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async () => {
await
module "node:fs/promises"
fs
.
function mkdir(path: PathLike, options: MakeDirectoryOptions & {
recursive: true;
}): Promise<string | undefined> (+2 overloads)

Asynchronously creates a directory.

The optional options argument can be an integer specifying mode (permission and sticky bits), or an object with a mode property and a recursive property indicating whether parent directories should be created. Calling fsPromises.mkdir() when path is a directory that exists results in a rejection only when recursive is false.

import { mkdir } from 'node:fs/promises';
try {
const projectFolder = new URL('./test/project/', import.meta.url);
const createDir = await mkdir(projectFolder, { recursive: true });
console.log(`created ${createDir}`);
} catch (err) {
console.error(err.message);
}

@sincev10.0.0

mkdir
(
function dirname(path: string): string

Return the directory name of a path. Similar to the Unix dirname command.

@parampath the path to evaluate.

@throws{TypeError} if path is not a string.

dirname
(
path: string
path
), {
recursive: true

Indicates whether parent folders should be created. If a folder was created, the path to the first created folder will be returned.

@defaultfalse

recursive
: true });
await
module "node:fs/promises"
fs
.
function writeFile(file: PathLike | fs.FileHandle, data: string | NodeJS.ArrayBufferView | Iterable<string | NodeJS.ArrayBufferView> | AsyncIterable<string | NodeJS.ArrayBufferView> | Stream, options?: (ObjectEncodingOptions & {
mode?: Mode | undefined;
flag?: OpenMode | undefined;
flush?: boolean | undefined;
} & EventEmitter<T extends EventMap<...> = DefaultEventMap>.Abortable) | BufferEncoding | null): Promise<void>

Asynchronously writes data to a file, replacing the file if it already exists. data can be a string, a buffer, an AsyncIterable, or an Iterable object.

The encoding option is ignored if data is a buffer.

If options is a string, then it specifies the encoding.

The mode option only affects the newly created file. See fs.open() for more details.

Any specified FileHandle has to support writing.

It is unsafe to use fsPromises.writeFile() multiple times on the same file without waiting for the promise to be settled.

Similarly to fsPromises.readFile - fsPromises.writeFile is a convenience method that performs multiple write calls internally to write the buffer passed to it. For performance sensitive code consider using fs.createWriteStream() or filehandle.createWriteStream().

It is possible to use an AbortSignal to cancel an fsPromises.writeFile(). Cancelation is "best effort", and some amount of data is likely still to be written.

import { writeFile } from 'node:fs/promises';
import { Buffer } from 'node:buffer';
try {
const controller = new AbortController();
const { signal } = controller;
const data = new Uint8Array(Buffer.from('Hello Node.js'));
const promise = writeFile('message.txt', data, { signal });
// Abort the request before the promise settles.
controller.abort();
await promise;
} catch (err) {
// When a request is aborted - err is an AbortError
console.error(err);
}

Aborting an ongoing request does not abort individual operating system requests but rather the internal buffering fs.writeFile performs.

@sincev10.0.0

@paramfile filename or FileHandle

writeFile
(
path: string
path
,
data: string | Buffer<ArrayBufferLike>
data
);
return true;
}, `Write File: ${
path: string
path
}`);
export const
const jsonFileSafe: <T = unknown>(path: string) => Promise<Result<T, Error>>
jsonFileSafe
= <
function (type parameter) T in <T = unknown>(path: string): Promise<Result<T, Error>>
T
= unknown>(
path: string
path
: string) =>
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <T, Error>(fn: Promise<T> | (() => Promise<T>), operationName?: string) => Promise<Result<T, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async () => {
const
const content: string
content
= await
module "node:fs/promises"
fs
.
function readFile(path: PathLike | fs.FileHandle, options: ({
encoding: BufferEncoding;
flag?: OpenMode | undefined;
} & EventEmitter<T extends EventMap<T> = DefaultEventMap>.Abortable) | BufferEncoding): Promise<string> (+2 overloads)

Asynchronously reads the entire contents of a file.

@parampath A path to a file. If a URL is provided, it must use the file: protocol. If a FileHandle is provided, the underlying file will not be closed automatically.

@paramoptions An object that may contain an optional flag. If a flag is not provided, it defaults to 'r'.

readFile
(
path: string
path
, "utf8");
return
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.parse(text: string, reviver?: (this: any, key: string, value: any) => any): any

Converts a JavaScript Object Notation (JSON) string into an object.

@paramtext A valid JSON string.

@paramreviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is.

parse
(
const content: string
content
) as
function (type parameter) T in <T = unknown>(path: string): Promise<Result<T, Error>>
T
;
}, `Read JSON File: ${
path: string
path
}`);

Usage:

export function
function readCache(): Promise<Result<Cache, Error>>
readCache
() {
return
const jsonFileSafe: <Cache>(path: string) => Promise<Result<Cache, Error>>
jsonFileSafe
<
interface Cache
Cache
>("./cache.json")
}
const [
const cache: Cache | null
cache
,
const cacheErr: Error | null
cacheErr
] = await
function readCache(): Promise<Result<Cache, Error>>
readCache
();
if (!
const cacheErr: Error | null
cacheErr
) {
// Use cache
return
const cache: Cache
cache
;
} else {
// Handle error
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.error(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stderr with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const code = 5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr

If formatting elements (e.g. %d) are not found in the first string then util.inspect() is called on each argument and the resulting string values are concatenated. See util.format() for more information.

@sincev0.1.100

error
(
const cacheErr: Error
cacheErr
);
}
export function safeQuery<T>(run: () => Promise<T>, label = "DB Query") {
return tryCatch.async(run, label);
}
// Usage
const [rows, dbErr] = await safeQuery(() => pool.query<Row>("SELECT * FROM x"), "Fetch Rows");
export function
function safeTx<T>(run: () => Promise<T>): Promise<Result<{
data: T;
commit: string;
}, Error>>
safeTx
<
function (type parameter) T in safeTx<T>(run: () => Promise<T>): Promise<Result<{
data: T;
commit: string;
}, Error>>
T
>(
run: () => Promise<T>
run
: () =>
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
function (type parameter) T in safeTx<T>(run: () => Promise<T>): Promise<Result<{
data: T;
commit: string;
}, Error>>
T
>) {
return
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <{
data: T;
commit: string;
}, Error>(fn: Promise<{
data: T;
commit: string;
}> | (() => Promise<{
data: T;
commit: string;
}>), operationName?: string) => Promise<Result<{
data: T;
commit: string;
}, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async () => {
await
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
begin: () => Promise<void>
begin
();
const [
const data: T | null
data
,
const runError: Error | null
runError
] = await
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <T, Error>(fn: Promise<T> | (() => Promise<T>), operationName?: string) => Promise<Result<T, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(
run: () => Promise<T>
run
(), 'Run Transaction');
if (
const runError: Error | null
runError
) {
await
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
rollback: () => Promise<void>
rollback
();
throw
const runError: Error
runError
;
}
const [
const commit: string | null
commit
,
const commitError: Error | null
commitError
] = await
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <string, Error>(fn: Promise<string> | (() => Promise<string>), operationName?: string) => Promise<Result<string, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
commit: () => Promise<string>
commit
(), 'Commit Transaction');
if (
const commitError: Error | null
commitError
) {
await
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
rollback: () => Promise<void>
rollback
();
throw
const commitError: Error
commitError
;
}
return {
data: T
data
,
commit: string
commit
};
})
}
export const
const safeTx: <T>(run: () => Promise<T>) => Promise<Result<{
data: unknown;
commit: string;
}, Error>>
safeTx
= <
function (type parameter) T in <T>(run: () => Promise<T>): Promise<Result<{
data: unknown;
commit: string;
}, Error>>
T
>(
run: () => Promise<T>
run
: () =>
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
function (type parameter) T in <T>(run: () => Promise<T>): Promise<Result<{
data: unknown;
commit: string;
}, Error>>
T
>) =>
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <{
data: unknown;
commit: string;
}, Error>(fn: Promise<{
data: unknown;
commit: string;
}> | (() => Promise<{
data: unknown;
commit: string;
}>), operationName?: string) => Promise<Result<{
data: unknown;
commit: string;
}, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async (
run: any
run
=
run: any
run
) => {
await
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
begin: () => Promise<void>
begin
();
const [
const data: unknown
data
,
const runError: Error | null
runError
] = await
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <unknown, Error>(fn: Promise<unknown> | (() => Promise<unknown>), operationName?: string) => Promise<Result<unknown, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(
run: any
run
(), 'Run Transaction');
if (
const runError: Error | null
runError
) {
await
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
rollback: () => Promise<void>
rollback
();
throw
const runError: Error
runError
;
}
const [
const commit: string | null
commit
,
const commitError: Error | null
commitError
] = await
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <string, Error>(fn: Promise<string> | (() => Promise<string>), operationName?: string) => Promise<Result<string, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
commit: () => Promise<string>
commit
(), 'Commit Transaction');
if (
const commitError: Error | null
commitError
) {
await
const db: {
begin: () => Promise<void>;
commit: () => Promise<string>;
rollback: () => Promise<void>;
}
db
.
rollback: () => Promise<void>
rollback
();
throw
const commitError: Error
commitError
;
}
return {
data: unknown
data
,
commit: string
commit
};
})
// Usage
const [
const txResult: {
data: unknown;
commit: string;
} | null
txResult
,
const txErr: Error | null
txErr
] = await
function safeTx<T>(run: () => Promise<T>): Promise<Result<{
data: T;
commit: string;
}, Error>>
safeTx
(async ():
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
interface User
User
> => {
// ...
})
if (!
const txErr: Error | null
txErr
) {
// Use txResult
return
const txResult: {
data: unknown;
commit: string;
}
txResult
;
} else {
// Handle error
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.error(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stderr with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const code = 5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr

If formatting elements (e.g. %d) are not found in the first string then util.inspect() is called on each argument and the resulting string values are concatenated. See util.format() for more information.

@sincev0.1.100

error
(
const txErr: Error
txErr
);
}

Layered Wrappers: Narrowing Further Upstream

Section titled “Layered Wrappers: Narrowing Further Upstream”

Build layers:

  1. Low-level raw boundary (fetch / fs).
  2. Mid-level parser / shape validator.
  3. High-level domain function returning rich domain object.

Example:

const
const fetchRawUser: (id: string) => Promise<Result<unknown, Error>>
fetchRawUser
= (
id: string
id
: string) =>
function safeFetch<unknown>(input: Parameters<typeof fetch>[0], { expectJson, operationName, ...init }?: SafeFetchOptions): Promise<Result<unknown, Error>>
safeFetch
<unknown>(`/api/users/${
id: string
id
}`, {
SafeFetchOptions.operationName?: string
operationName
: "Fetch Raw User" });
const
const parseUser: (raw: unknown) => Result<User, Error>
parseUser
= (
raw: unknown
raw
: unknown) =>
tryCatch<User, Error>(fn: User | (() => User), operationName?: string): Result<User, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
(() =>
const userSchema: {
parse: (raw: unknown) => User;
}
userSchema
.
parse: (raw: unknown) => User
parse
(
raw: unknown
raw
), "Validate User Schema");
export async function
function getUser(id: string): Promise<Result<User, Error>>
getUser
(
id: string
id
: string) {
return
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <User, Error>(fn: Promise<User> | (() => Promise<User>), operationName?: string) => Promise<Result<User, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async () => {
const [
const raw: unknown
raw
,
const fetchErr: Error | null
fetchErr
] = await
const fetchRawUser: (id: string) => Promise<Result<unknown, Error>>
fetchRawUser
(
id: string
id
);
if (
const fetchErr: Error | null
fetchErr
) throw
const fetchErr: Error
fetchErr
;
const [
const user: User | null
user
,
const parseErr: Error | null
parseErr
] =
const parseUser: (raw: unknown) => Result<User, Error>
parseUser
(
const raw: unknown
raw
);
if (
const parseErr: Error | null
parseErr
) throw
const parseErr: Error
parseErr
;
return
const user: User
user
;
}, 'Get User');
}

This produces separate, context-rich failures (Fetch vs Validate), instead of one big ambiguous error.


Sometimes you need to chain multiple dependent operations:

async function
function initConfiguredService(config: Config): Promise<Result<Service, Error>>
initConfiguredService
(
config: Config
config
:
interface Config
Config
) {
return
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <Service, Error>(fn: Promise<Service> | (() => Promise<Service>), operationName?: string) => Promise<Result<Service, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(async () => {
const [
const configText: string | null
configText
,
const readErr: Error | null
readErr
] = await
const readFileSafe: (path: string, encoding?: BufferEncoding) => Promise<Result<string, Error>>
readFileSafe
("./config.json");
if (
const readErr: Error | null
readErr
) throw
const readErr: Error
readErr
;
const [
const config: Config | null
config
,
const parseErr: Error | null
parseErr
] =
const parseJson: <Config>(raw: string, operationName?: string) => Result<Config, Error>
parseJson
<
interface Config
Config
>(
const configText: string
configText
, "Parse Config JSON");
if (
const parseErr: Error | null
parseErr
) throw
const parseErr: Error
parseErr
;
const [
const service: Service | null
service
,
const initErr: Error | null
initErr
] = await
const tryCatch: TryCatch<TryCatchFunc<Error>, Error>

tryCatch - Error handling that can be synchronous or asynchronous based on the input function.

@paramfn A function, promise, or value to execute within a try-catch block.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Result, or a Promise resolving to a Result, depending on fn.

tryCatch
.
async: <Service, Error>(fn: Promise<Service> | (() => Promise<Service>), operationName?: string) => Promise<Result<Service, Error>>

Executes an asynchronous function inside a try-catch block.

@paramfn The function or promise to execute.

@paramoperationName Optional name added to error.message for better debugging and context.

@returnsA Promise<Result<T, E>> indicating success or failure.

async
(
() =>
function initService(config: Config): Promise<Service>
initService
(
const config: Config
config
),
"Initialize Service"
);
if (
const initErr: Error | null
initErr
) throw
const initErr: Error
initErr
;
return
const service: Service
service
;
});
}
function
function ifError<E, T>(error: E | null, onError: (e: E) => void): error is E
ifError
<
function (type parameter) E in ifError<E, T>(error: E | null, onError: (e: E) => void): error is E
E
,
function (type parameter) T in ifError<E, T>(error: E | null, onError: (e: E) => void): error is E
T
>(
error: E | null
error
:
function (type parameter) E in ifError<E, T>(error: E | null, onError: (e: E) => void): error is E
E
| null,
onError: (e: E) => void
onError
: (
e: E
e
:
function (type parameter) E in ifError<E, T>(error: E | null, onError: (e: E) => void): error is E
E
) => void):
error: E | null
error
is
function (type parameter) E in ifError<E, T>(error: E | null, onError: (e: E) => void): error is E
E
{
if (
error: E | null
error
)
onError: (e: E) => void
onError
(
error: NonNullable<E>
error
);
return !!
error: E | null
error
;
}

Then:

Not working example
const [
const configText: string | null
configText
,
const readError: Error | null
readError
] = await
const readFileSafe: (path: string, encoding?: BufferEncoding) => Promise<Result<string, Error>>
readFileSafe
("./config.json");
if (
const ifError: <Error, unknown>(error: Error | null, onError: (e: Error) => void) => error is Error
ifError
(
const readError: Error | null
readError
,
function logAndAbort(): void
logAndAbort
)) return [
const configText: string | null
configText
,
const readError: Error
readError
];
configText;
const configText: string | null
readError;
const readError: null
const [
const config: Config | null
config
,
const parseError: Error | null
parseError
] =
const parseJson: <Config>(raw: string, operationName?: string) => Result<Config, Error>
parseJson
<
interface Config
Config
>(
const configText: string | null
configText
, "Parse Config");
if (
const ifError: <Error, unknown>(error: Error | null, onError: (e: Error) => void) => error is Error
ifError
(
const parseError: Error | null
parseError
,
function logAndAbort(): void
logAndAbort
)) return;
const [
const cfg: Config | null
cfg
] =
const parseJson: <Config>(raw: string, operationName?: string) => Result<Config, Error>
parseJson
<
interface Config
Config
>(
const configText: string | null
configText
, "Parse Config");

The tuple plugin allows [value, ,] when allowIgnoredError: true. Only use this when:

  • Failure is truly non-critical.
  • You log or degrade gracefully elsewhere.
  • The invariant does not become silently unsafe.
const [avatar, ,] = await safeFetch<Blob>(avatarUrl, {
operationName: "Fetch Avatar (Non-Critical)",
});

Prefer explicit comments:

// Intentionally ignoring error: fallback avatar used if fetch fails
const [avatar, ,] = await safeFetch<Blob>(avatarUrl, {
operationName: "Fetch Avatar",
});

Keep the contract: functions returning a Result should not throw (unless a truly unrecoverable programmer error). Pick one style per layer:

  • Outer API boundary (e.g., HTTP controller) often returns plain values & throws (framework catches).
  • Inner service/application layer can standardize on [data, error] for explicit branching.
  • Mixing patterns arbitrarily increases cognitive load.

Refactor incrementally:

  1. Identify a deep nesting chain.
  2. Extract each risky call into a wrapper returning tuple.
  3. Replace the nested try/catch with a linear sequence of [val, err] checks.
  4. Add operation names gradually.

Before:

try {
const user = await fetchUser();
const settings = await fetchSettings(user.id);
const theme = parseTheme(settings.themeJson);
return theme;
} catch (e) {
// ambiguous: which step failed?
return defaultTheme;
}

After:

const [user, userErr] = await fetchUser();
if (userErr) return defaultTheme;
const [settings, settingsErr] = await fetchSettings(user.id);
if (settingsErr) return defaultTheme;
const [theme, themeErr] = parseTheme(settings.themeJson);
if (themeErr) return defaultTheme;
return theme;

  • Overhead is minimal: 1-3 function invocations + try/catch boundary.
  • In V8/modern runtimes, predictable structured error handling performs well.(source?)
  • Avoid wrapping ultra‑hot micro-ops (tight loops) unless they actually throw.

Anti-PatternBetter Approach
Passing raw Result deeper without unpackingUnpack early; propagate only refined data or new [data,error].
Wrapping code that cannot throw (pure arithmetic)Skip wrapper.
Building giant monolithic wrapper doing fetch + parse + validate + map + deriveLayer small wrappers; richer error context.
Ignoring error silently ([data, ,]) in critical logicAlways branch or log.

  • Name describes intent (not implementation details).
  • Handles success and rejects with explicit throw inside the wrapped function (or returns value).
  • Narrowed / annotated expected error types with .errors<>() if valuable.
  • Returns Result<T, E> only (never mixing thrown exceptions).
  • Unit tests cover at least one success and one representative failure.

Adopting tryCatch effectively is less about the primitive and more about systematic consistency:

  1. Wrap boundaries.
  2. Name operations.
  3. Decompose multi-step flows into linear, readable branches.
  4. Classify and narrow errors where it adds decision-making value.
  5. Avoid silent ignores unless consciously intentional and documented.

With these patterns, the tuple approach scales cleanly while retaining clarity—and the TypeScript tooling enforces correctness without sacrificing ergonomics.