Reference

API reference

Look up the constructors, result types, methods, tags, and callback signatures exported by @hulla/control.

On this page

Look up ok(), err(), tcf(), and the methods on a Result. Start with Results if you want to see how a caller handles success and failure.

ExportCallRuntime result
okok(value) or ok(value, tag)Ok<T, 'ok'> by default; supplied tag is used verbatim
errerr(error) or err(error, tag)Err<E, 'error'> by default; error may be any value
resultresult(value, config?)Err when isError(value) is true, otherwise Ok
tcftcf({ try, catch, finally?, ...tags })Executes a try callback and converts a caught value to Err

ok and err are the most direct choice when the function already knows its outcome. The optional tag replaces that branch’s default tag, including an empty string. These constructors do not throw or inspect their payload.

Constructor signatures

TypeScript
import type { Err, Ok } from '@hulla/control'

declare function ok<const T>(value: T): Ok<T>
declare function ok<const T, const Tag extends string>(
  value: T,
  tag: Tag
): Ok<T, Tag>
declare function ok<const T, const Tag extends string>(
  value: T,
  tag: Tag | undefined
): Ok<T, Tag | 'ok'>
declare function err<const E>(error: E): Err<E>
declare function err<const E, const Tag extends string>(
  error: E,
  tag: Tag
): Err<E, Tag>
declare function err<const E, const Tag extends string>(
  error: E,
  tag: Tag | undefined
): Err<E, Tag | 'error'>

When omitted, tags are "ok" and "error". A supplied tag replaces the default for that constructed branch.

result(value, config?) uses value instanceof Error when isError is omitted. Its error generic is constrained to E extends Error, and the custom guard must identify that same error type:

TypeScript
import { result } from '@hulla/control'

declare function readDocument(): unknown

type InvalidDocument = Error & { code: 'INVALID_DOCUMENT' }
const isInvalidDocument = (value: unknown): value is InvalidDocument =>
  value instanceof Error && 'code' in value && value.code === 'INVALID_DOCUMENT'

const outcome = result<
  unknown,
  InvalidDocument,
  'INVALID_DOCUMENT',
  'DOCUMENT'
>(readDocument(), {
  isError: isInvalidDocument,
  tagOk: 'DOCUMENT',
  tagError: 'INVALID_DOCUMENT',
})

The predicate classifies the value at runtime and narrows its type. An unknown input still leaves the success payload unknown; a narrower input union gives a narrower success type.

Tags are available through .tag and as literal types. The generic order is T, E, TagError, TagOk.

For strings or plain objects used as errors, choose err() and Result<T, E>. result() requires an Error subtype.

config accepts isError, tagOk, and tagError. Omitted tags use ok and error; providing only one tag customizes only that branch. The implementation chooses a branch by calling isError(value) once.

result overloads accept optional configuration. Its custom guard has the shape (value: T | E) => value is E, and E must extend Error. Tag generics are ordered T, E, TagError, TagOk; for example:

TypeScript
declare const value: string | Error
const tagged = result<string | Error, Error, 'BAD', 'GOOD'>(value, {
  tagError: 'BAD',
  tagOk: 'GOOD',
})

Runtime defaults apply independently when a tag is omitted. The public config shape is { isError?: (value: T | E) => value is E; tagError?: TagError; tagOk?: TagOk }.

MemberOn OkOn ErrReturn behavior
isOk() / isErr()Narrows the branchNarrows the branchBoolean type guard
.value / .errorSuccess payloadError payloadDirect payload access on the narrowed branch
.match(onOk, onErr)Calls onOkCalls onErrCallback output; promise payloads resolve before callback
.map(fn)Calls fn with successReturns same error resultNew Ok; nested result output stays nested
.mapErr(fn)Returns same success resultCalls fn with errorNew Err; nested result output stays nested
.pair()[value, undefined][undefined, error]Tuple, or promise of tuple for promise payload
.unwrap()Returns the stored valueReturns the stored errorDoes not throw an Err payload

map and mapErr preserve the original branch and tag. They do not flatten a result returned from a mapper. Their synchronous exceptions and rejected promise outputs propagate; use tcf() around work when those exceptions should become an Err.

pair() is convenient for tuple-oriented callers, but both tuple slots can be undefined when the active payload is itself undefined. Branch with isOk() or isErr() when that distinction matters. unwrap() is not a success-only accessor: narrow first if failure must not be mistaken for a value.

TypeScript
import type { Err, Ok, Result, Tagged } from '@hulla/control'

type User = { id: string; name: string }
type LoadResult = Result<User, Error, 'USER', 'LOAD_ERROR'>
type Success = Ok<User, 'USER'>
type Failure = Err<Error, 'LOAD_ERROR'>
type Reclassified = Tagged<Success, 'CACHED_USER'>

Ok<T, Tag = 'ok'> and Err<E, Tag = 'error'> describe one branch. Result<T, E, TagOk = 'ok', TagError = 'error'> is their union. Tagged<T, Tag> replaces the tag on an Ok or Err type while retaining its payload and branch. These are types only; use constructors to create runtime values.