> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cbrock.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Result

> Type-safe error handling without try/catch.

`Result<T, E>` represents either a success (`Ok<T>`) or a failure (`Err<E>`). Unlike `try/catch`, errors appear directly in the function signature, so TypeScript enforces that you handle them.

```typescript theme={null}
// The caller has no idea this can fail
function getUser(id: string): User { ... }

// The caller knows exactly what can go wrong
function getUser(id: string): Result<User, 'not_found' | 'db_error'> { ... }
```

## Creating results

```typescript theme={null}
import { chas } from 'ts-chas';

const success = chas.ok(42);          // Ok<number>
const failure = chas.err('not found'); // Err<string>

// Async variants
const asyncSuccess = chas.okAsync(42);           // ResultAsync<number, never>
const asyncFailure = chas.errAsync('not found'); // ResultAsync<never, string>
```

## Wrapping existing code

Use `tryCatch` to safely call any function that might throw:

```typescript theme={null}
const parsed = chas.tryCatch(
  () => JSON.parse(rawInput),
  (e) => `Invalid JSON: ${e}`
);
// Result<unknown, string>
```

Use `fromPromise` to wrap a `Promise` that might reject:

```typescript theme={null}
function fetchUser(id: string): chas.ResultAsync<User, string> {
  return chas.fromPromise(
    fetch(`/users/${id}`).then((r) => r.json()),
    (e) => `Failed to fetch user: ${e}`
  );
}
```

## Chaining

All chaining methods skip the transformation when the result is an `Err`, so errors short-circuit automatically.

### `.map()` and `.mapErr()`

Transform the value or the error without unwrapping:

```typescript theme={null}
const name = chas.ok({ name: 'Alice', age: 30 })
  .map((user) => user.name);
// Ok('Alice')

const louder = chas.err('oops')
  .mapErr((e) => e.toUpperCase());
// Err('OOPS')
```

### `.andThen()`

Chain a function that itself returns a `Result`. Use this when the next step can also fail:

```typescript theme={null}
function parseAge(raw: string): Result<number, string> {
  const n = parseInt(raw, 10);
  return isNaN(n) ? chas.err('not a number') : chas.ok(n);
}

function validateAge(age: number): Result<number, string> {
  return age >= 18 ? chas.ok(age) : chas.err('must be 18+');
}

const result = parseAge('25').andThen(validateAge);
// Ok(25)
```

### `.orElse()`

Recover from an error by returning a fallback `Result`:

```typescript theme={null}
const result = fetchUser('123')
  .orElse(() => chas.ok(GUEST_USER));
// If fetchUser fails, returns Ok(GUEST_USER)
```

## Side effects

Use `.tap()` and `.tapErr()` to run side effects without modifying the result:

```typescript theme={null}
const result = await fetchUser('123')
  .tap((user) => console.log('Fetched:', user.name))
  .tapErr((err) => console.error('Error:', err));
// The result value is unchanged
```

## Consuming results

### `.match()`

The cleanest way to handle both branches:

```typescript theme={null}
const message = result.match({
  ok: (user) => `Hello, ${user.name}`,
  err: (e) => `Error: ${e}`,
});
// TypeScript infers the return type as string
```

### Type narrowing with `.isOk()` / `.isErr()`

```typescript theme={null}
if (result.isOk()) {
  console.log(result.value); // value is typed as T
} else {
  console.error(result.error); // error is typed as E
}
```

### `.unwrap()` and `.unwrapOr()`

`.unwrap()` throws if the result is an `Err`. Use it only when you are certain the result is `Ok`, or as a last resort:

```typescript theme={null}
const value = result.unwrapOr('default');      // returns T or default
const value = result.unwrapOrElse((e) => ''); // compute fallback from error
const value = result.unwrap();                 // throws if Err — use carefully
```

<Warning>
  `.unwrap()` throws the contained error if the result is `Err`. Prefer `.match()`, `.unwrapOr()`, or type narrowing for safe access.
</Warning>

## Attaching debug context

Use `.context()` to annotate errors with information about where in the chain the failure occurred. Context accumulates in order from most recent to oldest:

```typescript theme={null}
const result = await fetchUser(id)
  .context('fetching user')
  .andThen((u) => loadPermissions(u))
  .context('loading permissions');

if (result.isErr()) {
  console.log(result.error._context);
  // ['loading permissions', 'fetching user']
}
```

## The built-in `.pipe()` method

`Result` has a `.pipe()` method that lets you pass the result through a sequence of functions:

```typescript theme={null}
const double = (r: Result<number, string>) => r.map((v) => v * 2);
const addTen = (r: Result<number, string>) => r.map((v) => v + 10);

const result = chas.ok(5).pipe(double, addTen);
// Ok(20)
```

<Tip>
  `.pipe()` on a `Result` is different from the standalone `pipe` function; it receives the full `Result`, not just the inner value.
</Tip>

## `ResultAsync` is awaitable

`ResultAsync<T, E>` implements `PromiseLike`, so you can `await` it directly to get a synchronous `Result<T, E>`:

```typescript theme={null}
const resultAsync = fetchUser('123'); // ResultAsync<User, string>

// Await to get a synchronous Result
const result = await resultAsync; // Result<User, string>

// Or chain further before awaiting
const name = await fetchUser('123')
  .map((u) => u.name)
  .mapErr((e) => e.toUpperCase());
```

## Combining multiple results

### `chas.shapeAsync()`

Combine several `ResultAsync` values into a single shaped object. If any input is an `Err`, the first error is returned:

```typescript theme={null}
const dashboardData = await chas.shapeAsync({
  user: fetchUser(id),         // ResultAsync<User, ApiError>
  posts: fetchPosts(id),       // ResultAsync<Post[], ApiError>
  settings: fetchSettings(id), // ResultAsync<Settings, ApiError>
});
// Result<{ user: User; posts: Post[]; settings: Settings }, ApiError>

if (dashboardData.isOk()) {
  const { user, posts, settings } = dashboardData.value;
}
```

<Tabs>
  <Tab title="Sync: chas.shape()">
    ```typescript theme={null}
    const result = chas.shape({
      name: is.string.parse(data.name),
      age: is.number.parse(data.age),
    });
    // Result<{ name: string; age: number }, GuardErr>
    ```
  </Tab>

  <Tab title="Async: chas.shapeAsync()">
    ```typescript theme={null}
    const result = await chas.shapeAsync({
      user: fetchUser(id),
      config: fetchConfig(),
    });
    // Result<{ user: User; config: Config }, ApiError>
    ```
  </Tab>
</Tabs>

### Parallel combinators

```typescript theme={null}
// Succeeds if ALL succeed (short-circuits on first Err)
const all = await chas.allAsync([fetchUser('1'), fetchUser('2')]);
// Result<[User, User], ApiError>

// Collects ALL errors instead of short-circuiting
const collected = await chas.collectAsync([fetchUser('1'), fetchUser('2')]);
// Result<[User, User], ApiError[]>

// Succeeds with the first Ok, or Err with all errors
const any = await chas.anyAsync([fetchUser('1'), fetchUser('2')]);
// Result<User, ApiError[]>

// Resolves with whichever completes first
const race = await chas.raceAsync([fetchUser('1'), fetchUser('2')]);
// Result<User, ApiError>
```

## Do-notation with `chas.go()`

Use `chas.go()` to write sequential Result-based code without deeply nested `.andThen` chains. Yielding an `Err` short-circuits immediately:

```typescript theme={null}
const profileResult = await chas.go(async function* () {
  const user = yield* fetchUserAsync(id);      // short-circuits if Err
  const perms = yield* loadPermissions(user);  // also short-circuits
  return { user, perms };
});
// ResultAsync<{ user: User; perms: Permissions }, ApiError>
```
