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
}ResultErr
type ResultErr<E = unknown> = {
readonly ok: false
readonly issues: E
}Specialized result types
Each registry method returns a Result with specific value, error, and metadata types:
| Method | Result 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
}| Field | Type | Description |
|---|---|---|
code | DobaIssueCode | Machine-readable error category. |
message | string | Human-readable description. |
path | readonly PropertyKey[] | Property path where the issue occurred, if applicable. |
meta | Record<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'| Code | When | Extra metadata |
|---|---|---|
validation_failed | Schema validation rejected the value (input, intermediate, or output). | path: property path to the invalid field |
transform_failed | A migration function threw an error. | |
no_path_found | No migration path exists between the source and target schemas. | reachableFromSource, reachableToTarget |
unknown_schema | A schema key passed to transform, validate, etc. is not registered. | |
invalid_input | The value or options passed to a method are structurally invalid. | |
identify_failed | No guard matched and no tryParse schema validated the value. | |
identify_ambiguous | Multiple 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>): Timport { unwrap } from 'dobajs/result'
const data = unwrap(result) // throws Error if !result.okunwrapOr()
Extracts the value from a successful result, or returns a default fallback.
function unwrapOr<T, E, M>(result: Result<T, E, M>, defaultValue: T): Timport { 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))