Result
Result<A, E> represents a computation that either succeeded with a value of
type A or failed with an error of type E. It is a Success<A, E> or a
Failure<A, E>. Use it for synchronous operations that can fail in a known
way — parsing, validation, decoding — when you want the failure in the return
type rather than as a thrown exception.
In Effect v4, Result replaces v3’s Either. The success type comes first
(Result<A, E>, mirroring Effect<A, E, R>), the constructors are
Result.succeed / Result.fail, and the payloads are read via .success and
.failure.
import { Result } from "effect"
// A synchronous parser that reports failure as a value, not an exceptionconst parseAge = (input: string): Result.Result<number, string> => { const n = Number(input) return Number.isInteger(n) && n >= 0 ? Result.succeed(n) : Result.fail(`"${input}" is not a valid age`)}
const message = Result.match(parseAge("42"), { onFailure: (error) => `Invalid: ${error}`, onSuccess: (age) => `Parsed age ${age}`})
console.log(message) // "Parsed age 42"parseAge never throws. Its signature, Result<number, string>, tells callers
both outcomes are possible, and Result.match forces them to handle each.
Creating a Result
Section titled “Creating a Result”import { Result } from "effect"
const ok = Result.succeed(42) // { _id: 'Result', _tag: 'Success', success: 42 }const err = Result.fail("not found") // { _id: 'Result', _tag: 'Failure', failure: 'not found' }Result.fromOption lifts an Option into a Result, supplying the failure to
use for None:
import { Option, Result } from "effect"
const required = Result.fromOption( Option.fromNullishOr(process.env.PORT), () => "PORT is not set")Guards
Section titled “Guards”Result.isSuccess and Result.isFailure narrow the type so you can access the
payload safely:
import { Result } from "effect"
const result: Result.Result<number, string> = Result.succeed(42)
if (Result.isFailure(result)) { console.log(`Failed: ${result.failure}`)} else { console.log(`Succeeded: ${result.success}`)}// "Succeeded: 42"Transforming
Section titled “Transforming”Result.map transforms a success and passes failures through unchanged;
Result.mapError does the opposite. Result.flatMap chains a step that itself
returns a Result, short-circuiting on the first failure.
import { Result } from "effect"
const parse = (s: string): Result.Result<number, string> => Number.isNaN(Number(s)) ? Result.fail(`not a number: ${s}`) : Result.succeed(Number(s))
const positive = (n: number): Result.Result<number, string> => n > 0 ? Result.succeed(n) : Result.fail("must be positive")
// Chain two fallible steps; either failure stops the pipelineconst program = parse("10").pipe( Result.flatMap(positive), Result.map((n) => n * 2))
console.log(program) // { _id: 'Result', _tag: 'Success', success: 20 }Extracting the value
Section titled “Extracting the value”import { Result } from "effect"
// Supply a default for the failure caseconsole.log(Result.getOrElse(Result.fail("oops"), (e) => `recovered from ${e}`))// "recovered from oops"
// Collapse into a single union valueconsole.log(Result.merge(Result.fail("oops"))) // "oops"
// Throw the raw failure — use only where failure is impossibleconsole.log(Result.getOrThrow(Result.succeed(1))) // 1Generator syntax
Section titled “Generator syntax”Result.gen reads like Effect.gen but short-circuits on the first Failure.
Each yield* either unwraps a success or aborts the block with that failure.
import { Result } from "effect"
const parse = (s: string): Result.Result<number, string> => Number.isNaN(Number(s)) ? Result.fail(`not a number: ${s}`) : Result.succeed(Number(s))
const program = Result.gen(function* () { const x = yield* parse("10") const y = yield* parse("5") return x + y})
console.log(program) // { _id: 'Result', _tag: 'Success', success: 15 }Interop with Effect
Section titled “Interop with Effect”A Result can be used directly inside Effect.gen: a Success yields its
value, a Failure fails the effect with E. This makes Result a natural way
to write synchronous, fallible steps inside an effectful workflow without
juggling two error channels.
import { Effect, Result } from "effect"
const parse = (s: string): Result.Result<number, string> => Number.isNaN(Number(s)) ? Result.fail(`not a number: ${s}`) : Result.succeed(Number(s))
const program = Effect.gen(function* () { // A Failure here propagates as an Effect failure of type string const x = yield* parse("10") const y = yield* parse("5") return x + y})
Effect.runPromise(program).then(console.log) // 15Result vs Exit
Section titled “Result vs Exit”Result is for synchronous, expected failures of type E. When you run an
effect, the outcome is an Exit, which wraps a
Cause rather than a bare E — it can also represent defects and
interruptions. Reach for Result in pure code, and for Exit when inspecting
the result of executing an Effect. See
Result and Exit for how the two relate in
error management.
API reference
Section titled “API reference”Everything below is a public export of the Result module. Each entry has a
short, runnable example with inline // => comments. Most data-last
combinators (map, flatMap, …) are dual: you can call them
Result.map(self, f) or in a pipeline as self.pipe(Result.map(f)).
Constructors
Section titled “Constructors”succeed
Section titled “succeed”Lifts a value into a successful Result. The error type defaults to never.
import { Result } from "effect"
Result.succeed(42)// => { _id: 'Result', _tag: 'Success', success: 42 }Creates a failed Result. The success type defaults to never.
import { Result } from "effect"
Result.fail("boom")// => { _id: 'Result', _tag: 'Failure', failure: 'boom' }A pre-built Result<void> that succeeds with undefined. Use when a success
should signal completion without carrying a meaningful value.
import { Result } from "effect"
const done: Result.Result<void> = Result.voidResult.isSuccess(done)// => truefailVoid
Section titled “failVoid”A pre-built Result<never, void> whose failure value is undefined. Use when a
failure is only a control signal and no payload is needed.
import { Result } from "effect"
Result.failVoid// => { _id: 'Result', _tag: 'Failure', failure: undefined }succeedNone
Section titled “succeedNone”A pre-built Result<Option<never>> that succeeds with None. Handy as the
absent branch when working with transposeOption.
import { Result } from "effect"
Result.succeedNone// => { _id: 'Result', _tag: 'Success', success: { _id: 'Option', _tag: 'None' } }succeedSome
Section titled “succeedSome”Creates a Result<Option<A>> that succeeds with Some(a).
import { Result } from "effect"
Result.succeedSome(42)// => { _id: 'Result', _tag: 'Success', success: { _id: 'Option', _tag: 'Some', value: 42 } }Runs a synchronous computation that may throw, capturing the thrown value as a
Failure. With a single function the failure type is unknown; with
{ try, catch } the catch function maps the thrown value to E.
import { Result } from "effect"
Result.try(() => JSON.parse('{"ok":true}'))// => { _id: 'Result', _tag: 'Success', success: { ok: true } }
Result.try({ try: () => JSON.parse("not json"), catch: (e) => `Parse failed: ${e}`})// => { _id: 'Result', _tag: 'Failure', failure: 'Parse failed: SyntaxError: ...' }From other types
Section titled “From other types”fromNullishOr
Section titled “fromNullishOr”Turns a possibly null/undefined value into a Result. Non-nullish values
become Success<NonNullable<A>>; null/undefined become Failure<E> using
the onNullish callback (which receives the original value).
import { Result } from "effect"
Result.fromNullishOr(1, () => "missing")// => { _id: 'Result', _tag: 'Success', success: 1 }
Result.fromNullishOr(null, () => "missing")// => { _id: 'Result', _tag: 'Failure', failure: 'missing' }fromOption
Section titled “fromOption”Converts an Option<A> into a Result<A, E>. Some becomes Success; None
becomes Failure, with the error produced by the onNone thunk.
import { Option, Result } from "effect"
Result.fromOption(Option.some(1), () => "missing")// => { _id: 'Result', _tag: 'Success', success: 1 }
Result.fromOption(Option.none(), () => "missing")// => { _id: 'Result', _tag: 'Failure', failure: 'missing' }transposeOption
Section titled “transposeOption”Swaps Option<Result<A, E>> into Result<Option<A>, E>. None becomes
Success(None), Some(Success(a)) becomes Success(Some(a)), and
Some(Failure(e)) becomes Failure(e).
import { Option, Result } from "effect"
Result.transposeOption(Option.some(Result.succeed(42)))// => Success(Some(42))
Result.transposeOption(Option.none<Result.Result<number, string>>())// => Success(None)
Result.transposeOption(Option.some(Result.fail("err")))// => { _id: 'Result', _tag: 'Failure', failure: 'err' }transposeMapOption
Section titled “transposeMapOption”Maps an Option value with a Result-returning function and transposes in one
step, producing Result<Option<B>, E>. None short-circuits to
Success(None) without calling the function.
import { Option, Result } from "effect"
const parse = (s: string) => isNaN(Number(s)) ? Result.fail("not a number") : Result.succeed(Number(s))
Result.transposeMapOption(Option.some("42"), parse)// => Success(Some(42))
Result.transposeMapOption(Option.none(), parse)// => Success(None)Guards
Section titled “Guards”isResult
Section titled “isResult”Type guard that returns true for any Result (Success or Failure),
narrowing unknown input to Result<unknown, unknown>.
import { Result } from "effect"
Result.isResult(Result.succeed(1))// => true
Result.isResult({ value: 1 })// => falseisSuccess
Section titled “isSuccess”Type guard narrowing a Result to Success<A, E>, after which .success is
accessible.
import { Result } from "effect"
const r: Result.Result<number, string> = Result.succeed(42)Result.isSuccess(r) ? r.success : -1// => 42isFailure
Section titled “isFailure”Type guard narrowing a Result to Failure<A, E>, after which .failure is
accessible.
import { Result } from "effect"
const r: Result.Result<number, string> = Result.fail("oops")Result.isFailure(r) ? r.failure : ""// => "oops"Extract payload as Option
Section titled “Extract payload as Option”getSuccess
Section titled “getSuccess”Extracts the success value as an Option, discarding the failure.
Success<A> becomes Some<A>; Failure<E> becomes None.
import { Result } from "effect"
Result.getSuccess(Result.succeed("ok"))// => { _id: 'Option', _tag: 'Some', value: 'ok' }
Result.getSuccess(Result.fail("err"))// => { _id: 'Option', _tag: 'None' }getFailure
Section titled “getFailure”Extracts the failure value as an Option, discarding the success.
Failure<E> becomes Some<E>; Success<A> becomes None.
import { Result } from "effect"
Result.getFailure(Result.fail("err"))// => { _id: 'Option', _tag: 'Some', value: 'err' }
Result.getFailure(Result.succeed("ok"))// => { _id: 'Option', _tag: 'None' }Transforming
Section titled “Transforming”Transforms the success value, passing failures through unchanged.
import { Result } from "effect"
Result.map(Result.succeed(3), (n) => n * 2)// => { _id: 'Result', _tag: 'Success', success: 6 }
Result.map(Result.fail("err"), (n: number) => n * 2)// => { _id: 'Result', _tag: 'Failure', failure: 'err' }mapError
Section titled “mapError”Transforms the failure value, passing successes through unchanged.
import { Result } from "effect"
Result.mapError(Result.fail("not found"), (e) => `Error: ${e}`)// => { _id: 'Result', _tag: 'Failure', failure: 'Error: not found' }mapBoth
Section titled “mapBoth”Transforms both channels at once with { onSuccess, onFailure }, without
changing whether the result succeeds or fails.
import { Result } from "effect"
Result.mapBoth(Result.succeed(1), { onSuccess: (n) => n + 1, onFailure: (e: string) => `Error: ${e}`})// => { _id: 'Result', _tag: 'Success', success: 2 }flatMap
Section titled “flatMap”Chains a function returning a Result onto a success, merging the error types
into a union. Short-circuits on the first Failure. This is the monadic bind.
import { Result } from "effect"
Result.flatMap(Result.succeed(5), (n) => n > 0 ? Result.succeed(n * 2) : Result.fail("not positive"))// => { _id: 'Result', _tag: 'Success', success: 10 }andThen
Section titled “andThen”A flexible flatMap that also accepts a plain value, a plain-returning
function, or a Result directly. If self is a Failure, the argument is
never evaluated.
import { Result } from "effect"
Result.andThen(Result.succeed(1), (n) => Result.succeed(n + 1))// => Success(2)
Result.andThen(Result.succeed(1), (n) => n + 1)// => Success(2)
Result.andThen(Result.succeed(1), "done")// => Success("done")Swaps the success and failure channels, turning Result<A, E> into
Result<E, A>.
import { Result } from "effect"
Result.flip(Result.succeed(42))// => { _id: 'Result', _tag: 'Failure', failure: 42 }
Result.flip(Result.fail("error"))// => { _id: 'Result', _tag: 'Success', success: 'error' }Runs a side-effect on the success value (e.g. logging) and returns the original
Result unchanged. The callback’s return value is ignored; it is not called on
a Failure.
import { Result } from "effect"
Result.tap(Result.succeed(42), (n) => console.log("Got:", n))// logs: "Got: 42"// => { _id: 'Result', _tag: 'Success', success: 42 }Filtering / lifting
Section titled “Filtering / lifting”liftPredicate
Section titled “liftPredicate”Builds a Result from a raw value guarded by a predicate or refinement. Passing
values become Success; failing ones use orFailWith to produce the error. A
Refinement narrows the success type.
import { Result } from "effect"
Result.liftPredicate(5, (n: number) => n > 0, (n) => `${n} is not positive`)// => { _id: 'Result', _tag: 'Success', success: 5 }
Result.liftPredicate(-1, (n: number) => n > 0, (n) => `${n} is not positive`)// => { _id: 'Result', _tag: 'Failure', failure: '-1 is not positive' }filterOrFail
Section titled “filterOrFail”Validates the success value of an existing Result with a predicate or
refinement. An existing Failure passes through; a passing predicate keeps the
Success; a failing one produces a new Failure via orFailWith. The output
error type is the union of both.
import { Result } from "effect"
Result.filterOrFail( Result.succeed(0), (n) => n > 0, (n) => `${n} is not positive`)// => { _id: 'Result', _tag: 'Failure', failure: '0 is not positive' }Unwrapping
Section titled “Unwrapping”Returns the inner value as A | E regardless of the variant — useful when both
channels share a compatible type.
import { Result } from "effect"
Result.merge(Result.succeed(42))// => 42
Result.merge(Result.fail("error"))// => "error"getOrElse
Section titled “getOrElse”Returns the success value, or computes a fallback from the failure value.
import { Result } from "effect"
Result.getOrElse(Result.succeed(1), () => 0)// => 1
Result.getOrElse(Result.fail("err"), (e) => `recovered: ${e}`)// => "recovered: err"getOrNull
Section titled “getOrNull”Returns the success value, or null on failure.
import { Result } from "effect"
Result.getOrNull(Result.succeed(1))// => 1
Result.getOrNull(Result.fail("err"))// => nullgetOrUndefined
Section titled “getOrUndefined”Returns the success value, or undefined on failure.
import { Result } from "effect"
Result.getOrUndefined(Result.succeed(1))// => 1
Result.getOrUndefined(Result.fail("err"))// => undefinedgetOrThrow
Section titled “getOrThrow”Returns the success value, or throws the raw failure value E. Use only at
boundaries where failure is unexpected.
import { Result } from "effect"
Result.getOrThrow(Result.succeed(1))// => 1
Result.getOrThrow(Result.fail("error"))// => throws "error"getOrThrowWith
Section titled “getOrThrowWith”Returns the success value, or throws a custom error derived from the failure.
import { Result } from "effect"
Result.getOrThrowWith(Result.succeed(1), () => new Error("fail"))// => 1
Result.getOrThrowWith(Result.fail("oops"), (e) => new Error(`Unexpected: ${e}`))// => throws Error("Unexpected: oops")Pattern matching
Section titled “Pattern matching”Folds a Result into a single value by applying onSuccess or onFailure.
Both branches must produce a common type.
import { Result } from "effect"
const format = Result.match({ onSuccess: (n: number) => `Got ${n}`, onFailure: (e: string) => `Err: ${e}`})
format(Result.succeed(42))// => "Got 42"
format(Result.fail("timeout"))// => "Err: timeout"Fallbacks
Section titled “Fallbacks”orElse
Section titled “orElse”Returns the original Result if it is a Success; otherwise applies that to
the error to produce a recovery Result.
import { Result } from "effect"
Result.orElse(Result.fail("primary failed"), () => Result.succeed(99))// => { _id: 'Result', _tag: 'Success', success: 99 }
Result.orElse(Result.succeed(1), () => Result.succeed(99))// => { _id: 'Result', _tag: 'Success', success: 1 }Combining
Section titled “Combining”Collects a tuple, struct, or iterable of Results into a single Result of
the collected values, preserving structure. Short-circuits on the first
Failure — later elements are not inspected.
import { Result } from "effect"
// TupleResult.all([Result.succeed(1), Result.succeed("two")])// => Success([1, "two"])
// StructResult.all({ x: Result.succeed(1), y: Result.fail("err") })// => { _id: 'Result', _tag: 'Failure', failure: 'err' }
// IterableResult.all(new Set([Result.succeed(1), Result.succeed(2)]))// => Success([1, 2])Equivalence
Section titled “Equivalence”makeEquivalence
Section titled “makeEquivalence”Builds an Equivalence for Result from equivalences for the success and
failure types. Two Success values are equal by the success equivalence, two
Failure values by the failure equivalence, and a Success is never equal to
a Failure.
import { Equivalence, Result } from "effect"
const eq = Result.makeEquivalence( Equivalence.strictEqual<number>(), Equivalence.strictEqual<string>())
eq(Result.succeed(1), Result.succeed(1))// => true
eq(Result.succeed(1), Result.fail("x"))// => falseDo-notation & generators
Section titled “Do-notation & generators”Generator-based composition. yield* unwraps a Success or short-circuits on a
Failure; the generator’s return value is wrapped in Success. Evaluated
eagerly and synchronously.
import { Result } from "effect"
Result.gen(function* () { const a = yield* Result.succeed(1) const b = yield* Result.succeed(2) return a + b})// => { _id: 'Result', _tag: 'Success', success: 3 }The starting point for do-notation: a Result<{}> (success with an empty
object) to which you add named fields with bind and let.
import { pipe, Result } from "effect"
pipe( Result.Do, Result.bind("x", () => Result.succeed(2)), Result.bind("y", () => Result.succeed(3)), Result.let("sum", ({ x, y }) => x + y))// => Success({ x: 2, y: 3, sum: 5 })Adds a named field to the do-notation accumulator by running a
Result-producing function over the current object. Short-circuits on the first
Failure.
import { pipe, Result } from "effect"
pipe( Result.Do, Result.bind("x", () => Result.succeed(2)), Result.bind("y", ({ x }) => Result.succeed(x + 3)))// => Success({ x: 2, y: 5 })Adds a named field to the do-notation accumulator from a pure (non-Result)
computation over the current object.
import { pipe, Result } from "effect"
pipe( Result.Do, Result.bind("x", () => Result.succeed(2)), Result.let("doubled", ({ x }) => x * 2))// => Success({ x: 2, doubled: 4 })bindTo
Section titled “bindTo”Wraps the success value of an existing Result into a named field — typically
to start a do-notation chain from a Result you already have.
import { pipe, Result } from "effect"
pipe(Result.succeed(42), Result.bindTo("answer"))// => Success({ answer: 42 })See also
Section titled “See also”- Option — represent optional values; convert with
fromOption/getSuccess/transposeOption. - Exit and Cause — the outcome of running an
Effect, with a fullCauseinstead of a bareE. - Result and Exit — using both in error management.