doba

Result

Functional error handling with discriminated unions

doba uses a functional approach to error handling. Instead of throwing exceptions, operations return a Result type. All result utilities are available from dobajs/result.

Result

Discriminated union of success and failure:

type Result<T, E = unknown, M = unknown> = ResultOk<T, M> | ResultErr<E>

Check result.ok to narrow:

if (result.ok) {
  result.value // T
  result.meta  // M
} else {
  result.issues // E
}

ResultOk

type ResultOk<T, M = unknown> = {
  readonly ok: true
  readonly value: T
  readonly meta: M
}
ok: true
value: { id: "user-123", email: "alice@example.com" }
meta: {
path: ["database", "frontend"]
steps: 1
warnings: []
}

ResultErr

type ResultErr<E = unknown> = {
  readonly ok: false
  readonly issues: E
}
ok: false
issues:
- Expected string, received number at "id"
- Invalid email format at "email"

Specialized result types

Each registry method returns a Result with specific value, error, and metadata types:

MethodResult type
transform()TransformResult<T, Keys, From, To> = Result<T, DobaIssue[], TransformMeta>
validate()ValidateResult<T, Key> = Result<T, DobaIssue[], ValidateMeta>
identify()IdentifyResult<Keys> = Result<Keys, DobaIssue[], IdentifyMeta>
identifyAndTransform()IdentifyTransformResult<T, Keys> = Result<T, DobaIssue[], IdentifyTransformMeta>

See Registry API and Identify API for the full type definitions of each.

DobaIssue

All issues returned by doba use this shape:

type DobaIssue = {
  readonly code: DobaIssueCode
  readonly message: string
  readonly path?: readonly PropertyKey[] | undefined
  readonly meta?: Record<string, unknown> | undefined
}
FieldTypeDescription
codeDobaIssueCodeMachine-readable error category.
messagestringHuman-readable description.
pathreadonly PropertyKey[]Property path where the issue occurred, if applicable.
metaRecord<string, unknown>Arbitrary extra context (e.g. reachability info).

DobaIssueCode

type DobaIssueCode =
  | 'validation_failed'
  | 'transform_failed'
  | 'no_path_found'
  | 'unknown_schema'
  | 'invalid_input'
  | 'identify_failed'
  | 'identify_ambiguous'
CodeWhenExtra metadata
validation_failedSchema validation rejected the value (input, intermediate, or output).path: property path to the invalid field
transform_failedA migration function threw an error.
no_path_foundNo migration path exists between the source and target schemas.reachableFromSource, reachableToTarget
unknown_schemaA schema key passed to transform, validate, etc. is not registered.
invalid_inputThe value or options passed to a method are structurally invalid.
identify_failedNo guard matched and no tryParse schema validated the value.
identify_ambiguousMultiple tryParse schemas validated the same value.matches: array of conflicting schema keys

Constructors

ok()

Creates a successful result:

function ok<T, M = undefined>(value: T, meta?: M): ResultOk<T, M>
import { ok } from 'dobajs/result'

const result = ok({ id: '1' }, { schema: 'v1' })

err()

Creates a failed result:

function err<E>(issues: E): ResultErr<E>
import { err } from 'dobajs/result'

const result = err([{ code: 'validation_failed', message: 'bad input' }])

Type guards

isOk()

function isOk<T, E, M>(result: Result<T, E, M>): result is ResultOk<T, M>

isErr()

function isErr<T, E, M>(result: Result<T, E, M>): result is ResultErr<E>
import { isOk, isErr } from 'dobajs/result'

if (isOk(result)) {
  console.log(result.value)
}

if (isErr(result)) {
  console.error(result.issues)
}

Utilities

unwrap()

Extracts the value from a successful result, or throws if it failed.

function unwrap<T, E, M>(result: Result<T, E, M>): T
import { unwrap } from 'dobajs/result'

const data = unwrap(result) // throws Error if !result.ok

unwrapOr()

Extracts the value from a successful result, or returns a default fallback.

function unwrapOr<T, E, M>(result: Result<T, E, M>, defaultValue: T): T
import { unwrapOr } from 'dobajs/result'

const data = unwrapOr(result, fallbackValue)

map()

Transforms the value if successful, passes errors through unchanged.

function map<T, U, E, M>(result: Result<T, E, M>, fn: (value: T) => U): Result<U, E, M>
import { map } from 'dobajs/result'

const email = map(result, (user) => user.email)

mapErr()

Transforms the issues if failed, passes successes through unchanged.

function mapErr<T, E, F, M>(result: Result<T, E, M>, fn: (issues: E) => F): Result<T, F, M>
import { mapErr } from 'dobajs/result'

const formatted = mapErr(result, (issues) => issues.map((i) => i.message))

On this page