Skip to main content

Overview

better-result provides type utilities to extract success and error types from Result instances. These utilities are essential when working with complex Result types or building generic functions.

InferOk

Extracts the success value type from a Result type.
R
Result<T, E>
required
The Result type to extract from
Return Type
T
The success value type T from Result<T, E>

Type Definition

type InferOk<R> = R extends Ok<infer T, unknown> ? T : never;

How It Works

InferOk uses a distributive conditional type to extract the success type:
  • For Ok<A, X> | Ok<B, Y>, returns A | B
  • For Result<number, Error>, returns number
  • For non-Result types, returns never

Usage Examples

import { Result, InferOk } from "better-result";

type UserResult = Result<{ id: string; name: string }, Error>;
type User = InferOk<UserResult>;
// User = { id: string; name: string }
InferOk is distributive, meaning it distributes over union types. This is particularly useful when working with multiple Result types in generator compositions.

InferErr

Extracts the error value type from a Result type.
R
Result<T, E>
required
The Result type to extract from
Return Type
E
The error value type E from Result<T, E>

Type Definition

type InferErr<R> = R extends Err<unknown, infer E> ? E : never;

How It Works

InferErr uses a distributive conditional type to extract the error type:
  • For Err<X, A> | Err<Y, B>, returns A | B
  • For Result<string, NotFoundError>, returns NotFoundError
  • For non-Result types, returns never

Usage Examples

import { Result, InferErr } from "better-result";

class ValidationError extends Error {}
class NetworkError extends Error {}

type ApiResult = Result<string, ValidationError | NetworkError>;
type Errors = InferErr<ApiResult>;
// Errors = ValidationError | NetworkError
Both InferOk and InferErr work seamlessly with Result.gen to automatically extract union types from all yielded Results.

Common Patterns

Type-Safe Result Handlers

import { Result, InferOk, InferErr } from "better-result";

type Handler<R extends Result<unknown, unknown>> = {
  onSuccess: (value: InferOk<R>) => void;
  onError: (error: InferErr<R>) => void;
};

function handle<R extends Result<unknown, unknown>>(
  result: R,
  handler: Handler<R>
): void {
  result.match({
    ok: handler.onSuccess,
    err: handler.onError,
  });
}

const result: Result<number, string> = Result.ok(42);
handle(result, {
  onSuccess: (n) => console.log(n * 2), // n: number
  onError: (e) => console.error(e.toUpperCase()), // e: string
});

Extracting Types from Complex Workflows

import { Result, InferOk, InferErr } from "better-result";

// Define workflow
const fetchUserWorkflow = (id: string) =>
  Result.gen(function* () {
    const user = yield* getUser(id); // Result<User, NotFoundError>
    const posts = yield* getPosts(user.id); // Result<Post[], DbError>
    const profile = yield* getProfile(user.id); // Result<Profile, ApiError>

    return Result.ok({ user, posts, profile });
  });

// Extract types automatically
type WorkflowResult = ReturnType<typeof fetchUserWorkflow>;
type SuccessData = InferOk<Awaited<WorkflowResult>>;
// SuccessData = { user: User, posts: Post[], profile: Profile }

type AllErrors = InferErr<Awaited<WorkflowResult>>;
// AllErrors = NotFoundError | DbError | ApiError

Generic Result Transformers

import { Result, InferOk } from "better-result";

function mapSuccess<R extends Result<unknown, unknown>, U>(
  result: R,
  fn: (value: InferOk<R>) => U
): Result<U, InferErr<R>> {
  return result.map(fn);
}

const numResult = Result.ok(42);
const doubled = mapSuccess(numResult, (n) => n * 2);
// doubled: Result<number, never>

See Also