Skip to content

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 exception
const 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.

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"
)

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"

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 pipeline
const program = parse("10").pipe(
Result.flatMap(positive),
Result.map((n) => n * 2)
)
console.log(program) // { _id: 'Result', _tag: 'Success', success: 20 }
import { Result } from "effect"
// Supply a default for the failure case
console.log(Result.getOrElse(Result.fail("oops"), (e) => `recovered from ${e}`))
// "recovered from oops"
// Collapse into a single union value
console.log(Result.merge(Result.fail("oops"))) // "oops"
// Throw the raw failure — use only where failure is impossible
console.log(Result.getOrThrow(Result.succeed(1))) // 1

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 }

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) // 15

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.