[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › Result

Type-safe error handling with [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result).

The problem with exceptions in JavaScript is that a caught value is always of
unknown type. We can't be sure all errors have been handled because the
TypeScript compiler can't tell us what might be thrown — we can't use
[exhaustiveCheck](https://evolu.dev/docs/api-reference/common/Function/functions/exhaustiveCheck).

Languages like Rust and Haskell model recoverable failures with types such as
`Result` and `Either`, making errors part of the return type.

TypeScript can express the same pattern with discriminated unions of plain
objects. In Evolu, domain error interfaces extend [Typed](https://evolu.dev/docs/api-reference/common/Type/interfaces/Typed) to avoid
repeating the literal `type` discriminant.

```ts
import {
  assertEqual,
  assertErr,
  assertType,
  err,
  exhaustiveCheck,
  type Result,
  type Typed,
} from "@evolu/common";

// TypeScript can't know what was thrown.
try {
  throw new Error("Not found");
} catch (error) {
  assertType<unknown, typeof error>();
}

// With Result, errors are part of the return type.
const doSomething = (): Result<number, InvalidInputError | NotFoundError> =>
  err({ type: "NotFound" });

interface InvalidInputError extends Typed<"InvalidInput"> {}

interface NotFoundError extends Typed<"NotFound"> {}

// With Result, the error type is known and exhaustiveCheck works.
// If we add another error type, TypeScript tells us it isn't handled.
const result = doSomething();
if (!result.ok) {
  switch (result.error.type) {
    case "NotFound":
      assertEqual(result.error, { type: "NotFound" });
      break;
    case "InvalidInput":
      assertEqual(result.error, { type: "InvalidInput" });
      break;
    default:
      exhaustiveCheck(result.error);
  }
}
assertErr(result, { type: "NotFound" });
```

A `Result` is either [Ok](https://evolu.dev/docs/api-reference/common/Result/interfaces/Ok) (success with a value) or [Err](https://evolu.dev/docs/api-reference/common/Result/interfaces/Err) (failure
with an error). Create them with [ok](https://evolu.dev/docs/api-reference/common/Result/functions/ok) and [err](https://evolu.dev/docs/api-reference/common/Result/functions/err).

```ts

type Result<T, E = never> = Ok<T> | Err<E>;

interface Ok<T> {
  readonly ok: true;
  readonly value: T;
}

interface Err<E> {
  readonly ok: false;
  readonly error: E;
}

const result: Result<number> = { ok: true, value: 1 };
assertEqual(result, { ok: true, value: 1 });
```

Use [trySync](https://evolu.dev/docs/api-reference/common/Result/functions/trySync) and [tryAsync](https://evolu.dev/docs/api-reference/common/Result/functions/tryAsync) to intentionally convert thrown values
and Promise rejections into typed, recoverable errors represented by
`Result`.

Do not wrap every API that throws or rejects in `Result`. If an error is
unrecoverable and the caller has no meaningful fallback, let it propagate.

Since `Result` is a plain object, imperative code works naturally.

```ts
import {
  assertOk,
  assertType,
  ok,
  type Result,
  type Typed,
} from "@evolu/common";

interface User {
  readonly id: string;
}

const getUser = (): Result<User, UserNotFoundError> => ok({ id: "user-1" });

interface UserNotFoundError extends Typed<"UserNotFound"> {}

interface Profile {
  readonly userId: string;
}

const getProfile = (userId: string): Result<Profile, ProfileNotFoundError> =>
  ok({ userId });

interface ProfileNotFoundError extends Typed<"ProfileNotFound"> {}

const getCurrentProfile = (): Result<
  Profile,
  UserNotFoundError | ProfileNotFoundError
> => {
  const user = getUser();
  if (!user.ok) return user;

  return getProfile(user.value.id);
};

const profile = getCurrentProfile();
assertType<
  Result<Profile, UserNotFoundError | ProfileNotFoundError>,
  typeof profile
>();
assertOk(profile, { userId: "user-1" });
```

Note `user` and `profile` are named after their success values, not after the
Result (`userResult`, `profileResult`).

If a Result has no success value, name it `result`. For several such
operations, use [allResult](https://evolu.dev/docs/api-reference/common/Result/functions/allResult) with `{ collect: false }`.

## Unrecoverable errors

Unrecoverable errors are failures the application can't handle meaningfully.
For example, a database failure is unrecoverable when the application has no
useful fallback, retry, or degraded mode.

Do not turn such failures into `Result` merely because the underlying API
throws or rejects. Let the exception or Promise rejection propagate to the
top-level handler so the application stops instead of continuing in a
potentially invalid state.

In Evolu apps, the root [Run](https://evolu.dev/docs/api-reference/common/Task/interfaces/Run) reports defects and the platform lifecycle
API owns application shutdown. For example, `@evolu/nodejs` provides
[runMain](https://evolu.dev/docs/api-reference/nodejs/functions/runMain).

## FAQ

### Is Result expensive?

Wrapping a value in Result creates one small plain object and is very cheap.
Its cost is usually worth considering only when a performance-critical loop
creates millions of Results. Use Result by default; if profiling identifies
such a loop as a bottleneck, benchmark the complete workload before
optimizing it.

### Why not generators?

Generator-based APIs make sequential workflows more concise: `yield*`
combines error propagation (roughly replacing `if (!result.ok) return
result`) with unwrapping the success value. That doesn't come for free.
Supporting direct `yield*` requires adding iterator behavior to every Result;
otherwise every use needs an adapter. That makes Result more than plain
structural data: after serialization, its iterator behavior must be restored
before direct `yield*` works. Generator machinery also makes control flow,
debugger stepping, and stack traces less direct while adding runtime
overhead. Evolu instead keeps Result as plain data and makes error
propagation explicit. With AI, explicit checks are cheap to write.

Generators do not make accidental omission impossible: a function returning a
lazy operation can be called without composing the returned value with
`yield*`, leaving the operation out of the workflow, just as a
Result-returning function can be called and its Result ignored. Dedicated
tooling can detect these omissions, but that safety comes from the tooling,
not generator syntax itself.

The intended way to write Evolu code is with test-driven development (TDD).
Tests document the intended behavior and serve as its runnable specification,
including failure paths. AI makes writing and maintaining those tests cheap.
[exhaustiveCheck](https://evolu.dev/docs/api-reference/common/Function/functions/exhaustiveCheck) complements tests by having TypeScript report newly
added error variants.

### What if a function doesn't return a value on success?

Use `Result<void, E>` and return `ok()` (no argument). Don't return
`ok(true)`, `ok("success")`, or `ok("done")` — `ok()` already signals
success; redundant values add noise.

## Core

| Name                                                                      | Description                                                                                                                                                                |
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Err](https://evolu.dev/docs/api-reference/common/Result/interfaces/Err)               | An error [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result).                                                                                              |
| [Ok](https://evolu.dev/docs/api-reference/common/Result/interfaces/Ok)                 | A successful [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result).                                                                                          |
| [AnyResult](https://evolu.dev/docs/api-reference/common/Result/type-aliases/AnyResult) | Shorthand for a [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result) with `any` type parameters.                                                            |
| [InferErr](https://evolu.dev/docs/api-reference/common/Result/type-aliases/InferErr)   | Infers the error type from a [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result).                                                                          |
| [InferOk](https://evolu.dev/docs/api-reference/common/Result/type-aliases/InferOk)     | Infers the success value type from a [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result).                                                                  |
| [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result)       | A discriminated success or failure value: either [Ok](https://evolu.dev/docs/api-reference/common/Result/interfaces/Ok) or [Err](https://evolu.dev/docs/api-reference/common/Result/interfaces/Err). |
| [err](https://evolu.dev/docs/api-reference/common/Result/functions/err)                | Creates an [Err](https://evolu.dev/docs/api-reference/common/Result/interfaces/Err) result.                                                                                             |
| [ok](https://evolu.dev/docs/api-reference/common/Result/functions/ok)                  | Creates an [Ok](https://evolu.dev/docs/api-reference/common/Result/interfaces/Ok) result.                                                                                               |

## Guards

| Function                                                       | Description                                                                         |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [isErr](https://evolu.dev/docs/api-reference/common/Result/functions/isErr) | Type guard for [Err](https://evolu.dev/docs/api-reference/common/Result/interfaces/Err) results. |
| [isOk](https://evolu.dev/docs/api-reference/common/Result/functions/isOk)   | Type guard for [Ok](https://evolu.dev/docs/api-reference/common/Result/interfaces/Ok) results.   |

## Unwrapping

| Function                                                                 | Description                                                                                                                                                             |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [getOk](https://evolu.dev/docs/api-reference/common/Result/functions/getOk)           | Gets the value from a [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result) whose error type is `never`.                                                  |
| [getOrNull](https://evolu.dev/docs/api-reference/common/Result/functions/getOrNull)   | Gets the value from an [Ok](https://evolu.dev/docs/api-reference/common/Result/interfaces/Ok), or returns `null` for an [Err](https://evolu.dev/docs/api-reference/common/Result/interfaces/Err). |
| [getOrThrow](https://evolu.dev/docs/api-reference/common/Result/functions/getOrThrow) | Gets the value from an [Ok](https://evolu.dev/docs/api-reference/common/Result/interfaces/Ok), or throws for an [Err](https://evolu.dev/docs/api-reference/common/Result/interfaces/Err).         |

## Composition

| Function                                                                       | Description                                                                                                                        |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| [allResult](https://evolu.dev/docs/api-reference/common/Result/functions/allResult)         | Collects successful values from [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result)s.                              |
| [anyResult](https://evolu.dev/docs/api-reference/common/Result/functions/anyResult)         | Returns the first successful [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result).                                  |
| [flatMapResult](https://evolu.dev/docs/api-reference/common/Result/functions/flatMapResult) | Composes a successful [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result) with another Result-returning operation. |

## Exception interop

| Function                                                             | Description                                                                                                                        |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| [tryAsync](https://evolu.dev/docs/api-reference/common/Result/functions/tryAsync) | Wraps an async function that may throw or reject, returning a [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result). |
| [trySync](https://evolu.dev/docs/api-reference/common/Result/functions/trySync)   | Wraps a synchronous function that may throw, returning a [Result](https://evolu.dev/docs/api-reference/common/Result/type-aliases/Result).      |

## Pull

| Name                                                                          | Description                                                                                                    |
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| [Done](https://evolu.dev/docs/api-reference/common/Result/interfaces/Done)                 | A signal indicating normal completion of a pull-based protocol.                                                |
| [ExcludeDone](https://evolu.dev/docs/api-reference/common/Result/type-aliases/ExcludeDone) | Removes [Done](https://evolu.dev/docs/api-reference/common/Result/interfaces/Done) from an error union.                     |
| [InferDone](https://evolu.dev/docs/api-reference/common/Result/type-aliases/InferDone)     | Infers the done value type from a [NextResult](https://evolu.dev/docs/api-reference/common/Result/type-aliases/NextResult). |
| [NextResult](https://evolu.dev/docs/api-reference/common/Result/type-aliases/NextResult)   | A result for a pull-based protocol with three outcomes.                                                        |
| [OnlyDone](https://evolu.dev/docs/api-reference/common/Result/type-aliases/OnlyDone)       | Extracts only [Done](https://evolu.dev/docs/api-reference/common/Result/interfaces/Done) from an error union.               |
| [done](https://evolu.dev/docs/api-reference/common/Result/functions/done)                  | Constructs a [Done](https://evolu.dev/docs/api-reference/common/Result/interfaces/Done) value.                              |