> ## 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.

# Option

> The Option<T> type for nullable values.

`Option<T>` models an optional value. It is either `Some<T>` (a value is present) or `None` (no value). Under the hood, `Option<T>` is an alias for `Result<NonNullable<T>, never>`, so it integrates directly with the rest of the `ts-chas` ecosystem.

```typescript theme={null}
import {
  some,
  none,
  nullable,
  optionFromGuard,
  type Option,
} from 'ts-chas/option';
```

***

## The `Some` and `None` types

| Type        | Description                                                                   |
| ----------- | ----------------------------------------------------------------------------- |
| `Some<T>`   | Represents a present, non-nullable value. Equivalent to `Ok<NonNullable<T>>`. |
| `None`      | Represents the absence of a value. Equivalent to `Err<never>`.                |
| `Option<T>` | The union: `Some<T> \| None`                                                  |

Because `Option` is built on `Result`, all `Result` methods are available on `Option` values.

***

## Factory functions

### `nullable(value)`

Returns `Some(value)` if the value is non-null and non-undefined, otherwise `None`. This is the most common way to create an `Option`.

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

const a = nullable('hello');    // Some('hello')
const b = nullable(null);       // None
const c = nullable(undefined);  // None
const token = nullable(localStorage.getItem('token')); // Some(string) | None
```

***

### `optionFromGuard(value, guard)`

Returns `Some(value)` if the type guard passes, `None` otherwise. Use this to narrow an `unknown` value into a typed `Option`.

```typescript theme={null}
import { optionFromGuard } from 'ts-chas/option';
import { is } from 'ts-chas/guard';

const raw: unknown = JSON.parse(stored);
const maybeNum = optionFromGuard(raw, is.number);
// Option<number>

if (maybeNum.isSome()) {
  console.log(maybeNum.value * 2); // typed as number
}
```

***

### `some(value)`

Explicitly constructs a `Some`. The value must be non-null and non-undefined.

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

const opt = some(42); // Some(42)
opt.value; // 42
```

***

### `none()`

Explicitly constructs a `None`.

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

const opt = none(); // None
opt.isNone(); // true
```

***

### Converting `Option` to `Result`

`Option` is `Result<NonNullable<T>, never>`. To assign a concrete error type to the `None` case, use `.orElse()`:

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

const maybeId = nullable(params.get('id'));
const result = maybeId.orElse(() => chas.err(new Error('Missing id parameter')));
// Result<string, Error>
```

Use `Task.fromOption` to lift an `Option` directly into an async pipeline — see [Converting to Task](#converting-to-task) below.

***

## Instance methods

Because `Option<T>` is a `Result`, you have the full `Result` method surface:

### `.isSome()` / `.isNone()`

Type-narrowing predicates.

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

const opt = nullable(user.email);

if (opt.isSome()) {
  sendEmail(opt.value); // opt.value is string here
}
```

***

### `.map(fn)`

Transforms the value inside `Some`. Has no effect on `None`.

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

const maybeLength = nullable('hello').map(s => s.length);
// Some(5)

const nothing = nullable<string>(null).map(s => s.length);
// None
```

***

### `.andThen(fn)`

`flatMap` for Options. `fn` receives the `Some` value and must return another `Option`.

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

const maybeUser = nullable(getUser());
const maybeEmail = maybeUser.andThen(user => nullable(user.email));
// Some(email) | None
```

***

### `.orElse(fn)`

Provides a fallback `Option` if the current one is `None`.

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

const maybeToken = nullable(localStorage.getItem('token'))
  .orElse(() => nullable(sessionStorage.getItem('token')));
// Tries session storage if local storage is empty
```

***

### `.unwrapOr(default)`

Returns the value if `Some`, or the default if `None`.

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

const name = nullable(user.displayName).unwrapOr('Anonymous');
```

***

### `.unwrap()`

Returns the value if `Some`, or throws if `None`. Prefer `.unwrapOr()` or `.isSome()` checks in production code.

```typescript theme={null}
const opt = some(42);
opt.unwrap(); // 42
```

***

### `.match({ some, none })`

Pattern-matches the option, returning the result of whichever branch applies.

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

const greeting = nullable(user.name).match({
  ok: name => `Hello, ${name}!`,
  err: () => 'Hello, stranger!',
});
```

<Note>
  Because `Option` is a `Result`, the branches are `ok` and `err` — mapping to `some` and `none` respectively.
</Note>

***

## Converting to Task

Use `Task.fromOption` to lift a synchronous `Option` into an async Task pipeline. If the option is `None`, the task fails with the provided error.

```typescript theme={null}
import { nullable } from 'ts-chas/option';
import { Task } from 'ts-chas/task';

// Synchronous cache lookup → Option
const checkCache = () => nullable(localStorage.getItem('user'));

// Convert to Task so it fits into an async pipeline
const cacheTask = Task.fromOption(
  checkCache(),
  () => new Error('Cache miss')
);

// Fall back to a network fetch if the cache is empty
const fetchTask = Task.from(
  () => fetch('/api/user').then(r => r.json()),
  () => new Error('Fetch failed')
);

const result = await cacheTask
  .fallback(fetchTask)
  .execute();
// Result<string, Error>
```

***

## Complete example

```typescript theme={null}
import { nullable } from 'ts-chas/option';
import { Task } from 'ts-chas/task';

interface Config {
  apiUrl: string;
  timeout: number;
}

function getStoredConfig(): Config | null {
  try {
    return JSON.parse(localStorage.getItem('config') ?? 'null');
  } catch {
    return null;
  }
}

const configTask = Task.fromOption(
  nullable(getStoredConfig()),
  () => new Error('No config in storage')
);

const fetchConfigTask = Task.from(
  () => fetch('/api/config').then(r => r.json() as Promise<Config>),
  () => new Error('Config fetch failed')
);

// Use cached config; fall back to network
const result = await configTask
  .fallback(fetchConfigTask)
  .tap(config => console.log('Using config:', config.apiUrl))
  .execute();

if (result.isOk()) {
  startApp(result.value);
} else {
  console.error('Could not load config:', result.error.message);
}
```
