# Catching exceptions with tcf

> Turn thrown exceptions and rejected promises into Result values.

## Useful when

- You call APIs that communicate failure by throwing or rejecting.
- You want a typed Result around synchronous or asynchronous work.

`JSON.parse()` and many other APIs throw when they fail. Wrap that work in `tcf()` when you want the caller to receive an `Err` instead of catching an exception.

## Convert a synchronous throw

For parsing a string in application code, the callback remains synchronous and `tcf()` returns a `Result` immediately. Catch input is `unknown` by default because JavaScript allows throwing any value.

```ts
import { tcf } from '@hulla/control'

const parsed = tcf({
  try: () => JSON.parse('{"name":"Sam"}') as { name: string },
  catch: (cause) => new Error(`Invalid JSON: ${String(cause)}`),
})

console.log(parsed.isOk()) // true
```

The cast above describes the expected shape to TypeScript; it does not validate JSON fields at runtime. `tcf()` catches thrown exceptions, but it is not a schema validator. Separate parsing from shape checks when reading untrusted JSON. This function returns a `Result` for malformed JSON and for a valid JSON value with the wrong shape:

```ts
import { err, ok, tcf, type Result } from '@hulla/control'

type Settings = { title: string }

function parseSettings(source: string): Result<Settings, Error> {
  const parsed = tcf({
    try: () => JSON.parse(source) as unknown,
    catch: (cause) => new Error(`Invalid JSON: ${String(cause)}`),
  })
  if (parsed.isErr()) return parsed

  const value = parsed.value
  if (
    typeof value !== 'object' ||
    value === null ||
    !('title' in value) ||
    typeof value.title !== 'string'
  ) {
    return err(new Error('Settings must include a string title'))
  }
  return ok({ title: value.title })
}
```

`JSON.parse` returns untrusted data; the `unknown` annotation avoids treating a cast as validation. The property checks establish the `Settings` shape before constructing `Ok`. A display caller might recover with a fallback, while a save operation could return the error:

```ts
const title = parseSettings(source).match(
  (settings) => settings.title,
  () => 'Untitled'
)
```

This fallback is appropriate for display. `tcf()` wraps the parser's thrown syntax error; explicit `err()` handles the domain validation failure.

## Await asynchronous work

When `try`, `catch`, or `finally` returns a promise, await the `tcf()` call. The following example runs in a browser and fetches `/data.txt` from the current site. It keeps a fetch failure inside the `Result` and returns the response text on success.

```ts
const downloaded = await tcf({
  try: async () => {
    const response = await fetch('/data.txt')
    if (!response.ok) throw new Error(`HTTP ${response.status}`)
    return response.text()
  },
  catch: (cause) => (cause instanceof Error ? cause : new Error(String(cause))),
  finally: () => console.log('Request finished'),
})

downloaded.match(
  (text) => console.log(text),
  (error) => console.error(error.message)
)
```

The awaited output is a `Result<string, Error>`. When every callback is synchronous, `tcf()` returns immediately. If `try`, `catch`, or `finally` returns a promise, await the call before narrowing.

Cleanup runs once after success or a converted error. A synchronous throw from `catch` still runs cleanup, then propagates.

> **Warning: Cleanup can replace the earlier outcome**
>
> If `finally` throws or rejects, that failure propagates instead of the earlier result. Keep cleanup small, and handle its expected failures inside the callback.

For expected domain failures, return `err()` directly. Read [Results](https://hulla.dev/docs/control/results.md) to handle the branch.

JavaScript can throw any value, so `cause` is `unknown`. Check it before reading properties such as `.message`. The `catch` callback must return an `Error` or a promise of one; that becomes the result's error value.

You can set `tagOk` and `tagError` just as you would on `ok()` and `err()`. See [Async results](https://hulla.dev/docs/control/async.md) for promise handling, or [Reference](https://hulla.dev/docs/control/reference.md) for the exact types.
