Migrate neverthrow to fault
Identical APIs
These names and roles match neverthrow. Import them from @thourum/fault.
ok, err, okAsync, errAsync, Result, ResultAsync, Ok, Err, fromThrowable, fromPromise, fromSafePromise, fromAsyncThrowable, safeTry, Result.combine, Result.combineWithAllErrors, ResultAsync.combine, ResultAsync.combineWithAllErrors, map, mapErr, andThen, orElse, match, unwrapOr, isOk, isErr, asyncAndThen, asyncMap
isOk / isErr / asyncAndThen / asyncMap live on Result only. ResultAsync has no isOk / isErr — await it first.
Rename table
neverthrow names are gone. fault renamed the tee/through family:
andCheck / asyncAndCheck: on Ok, run f. Inner Ok is discarded and the original T is kept. Inner Err replaces the result.
asyncAndCheck exists on Result only. On ResultAsync, andCheck already accepts Result | ResultAsync.
_unsafeUnwrap / _unsafeUnwrapErr still exist on Result (Ok / Err). They are absent on ResultAsync — await then unwrap, or use match / expect(r).toEqual(ok(x)) in tests.
Error type: E = Fault
neverthrow leaves E free. fault's convention is Result<T, Fault>.
ServiceError(tag, message, description?) is new Fault(message).withTag(tag).withDescription(description ?? message).
FaultTag is a predefined union plus (string & {}) for custom tags. Narrow with fault.tag, not e.kind.
Wrap foreign errors
Observability
capture() calls Fault.onCapture if set and returns the same fault. See docs: Fault (capture / toJSON) and Error tracing.
retry (fault-only)
neverthrow has no equivalent.
retry<T, E>(fn: () => ResultAsync<T, E>, opts: RetryOptions<E>): ResultAsync<T, E | Fault>
times is total attempts. A synchronous throw from fn, rejected ResultAsync, or throw from when resolves to Err(Fault) tagged UNKNOWN_ERROR, with the thrown value as cause, and stops retrying. To retry expected thrown failures, map them into tagged Err values with fromAsyncThrowable first. See docs: retry.
Gotchas
await resultAsyncyieldsResult<T, E>, notT.ResultAsyncisPromiseLike<Result<T, E>>.ResultAsynchas noisOk/isErr/asyncAndThen/asyncMap/asyncAndCheck/_unsafeUnwrap. Await, then use Result methods; useandThen/map/andCheckon the async side.err('x')uses the string-literal overload (Err<never, 'x'>). Prefererr(ServiceError('TAG', 'x'))soEstaysFault.andInspect/orInspectswallow throws fromf.andCheckdoes not.
Checklist
- Rewrite
from 'neverthrow'→from '@thourum/fault'. - Rename
andTee/orTee/andThrough/asyncAndThrough. - Change signatures to
Result<T, Fault>/ResultAsync<T, Fault>. - Replace
{ kind }unions withServiceError/withTag; branch onfault.tag. - Route
mapErrwrappers throughFault.from(...).withTag(...).withCause(...). - Set
Fault.onCaptureonce; call.capture()at the request/job edge. - Await
ResultAsyncbeforeisOk/isErr/_unsafeUnwrap. - Add
retryon transient tags (CONNECTION_ERROR,NETWORK_ERROR) where you used to hand-roll loops.