Either

SetoidFunctorApplyApplicativeAltChainMonadFoldableExtendBifunctor
Either is a data type with two sides (constructors) - Left and Right. It is most commonly used for error handling as it is very similar to the Maybe type with the only difference being that you can store information about the missing value (an error message for example). By convention, "Right is right", meaning that success is stored on the Right and failure is stored on the Left. It is also important to note that Either is right-biased which means that `map`, `chain` and other similar methods will operate on the right side.
How to import
import { Either, Left, Right } from 'purify-ts/Either'
Without Either
const getPort = () => {
    const config: Config | null = getConfig()

    if (config && config.port) {
        return config.port
    }

    throw Error("Couldn't parse port from config")
}

let port: number

try {
    port = parseInt(getPort())
} catch (e) {
    loggingService.log(e.message)
    port = 8080
}
With Either
const getPort = () => getConfig() // Maybe makes a great combo with Either
    .chain(x => x.port)
    .toEither(new Error("Couldn't parse port from config"))

const port: number = getPort() // Either<Error, number>
    .ifLeft((e) => loggingService.log(e.message))
    .map(parseInt)
    .orDefault(8080)

Constructors

Left
a -> Either a b<L>(value: L): Either<L, never>
Constructs a Left. Most commonly represents information about an operation that failed.
When creating Either instances using the Left and Right constructors and returning them from a function please don't forget to add a type annotation to that function.
Otherwise TypeScript is not smart enough to figure out the correct return type and you won't be able to use the return value as expected.
// Inferred type is randomEither: () => Either<never, number> | Either<string, never>
const randomEither = () =>
    Math.random() > 0.5 ? Right(1) : Left('Error')

randomEither().map(x => x)
//             ~~~
// This expression is not callable.
// Each member of the union type ... has signatures,
// but none of those signatures are compatible with each other.
Left('Error')
Left('Error') // Either<string, never>
Right
b -> Either a b<R>(value: R): Either<never, R>
Constructs a Right. Represents a successful result of an operation.
Right(10)
Right(10) // Either<never, number>

Static methods

of
b -> Either a b<R>(value: R): Either<never, R>
Takes a value and wraps it in a `Right`.
Either.of(5)
Right(5)
lefts
[Either a b] -> [a]<L, R>(list: Either<L,R>[]): L[]
Takes a list of eithers and returns a list of all `Left` values.
Either.lefts([Left('Server error'), Left('Wrong password'), Right('foo@bar.com')])
['Server error', 'Wrong password']
rights
[Either a b] -> [b]<L, R>(list: Either<L, R>[]): R[]
Takes a list of eithers and returns a list of all `Right` values.
Either.rights([Right(10), Left('Invalid input'), Right(5)])
[10, 5]
encase
<L extends Error, R>(throwsF: () => R): Either<L, R>
Calls a function and returns a `Right` with the return value or an exception wrapped in a `Left` in case of failure.
Either.encase(() => { throw Error('Always fails') })
Either.encase(() => 10)
Left(new Error('Always fails'))
Right(10)

Instance methods

isLeft
Either a b -> Bool(): boolean
Returns true if `this` is `Left`, otherwise it returns false.
Left('Error').isLeft()
Right(10).isLeft()
true
false
isRight
Either a b -> Bool(): boolean
Returns true if `this` is `Right`, otherwise it returns false.
Left('Error').isRight()
Right(10).isRight()
false
true
caseOf
<T>(patterns: { Left: (l: L) => T, Right: (r: R) => T }): T
Structural pattern matching for `Either` in the form of a function.
Left('Error').caseOf({ Left: x => x, Right: () => 'No error' })
Right(6).caseOf({ Left: _ => 0, Right: x => x + 1 })
Left('Error').caseOf({ _: () => 0 }) // wildcard
'Error'
7
0
bimap
Either a b ~> (a -> c, b -> d) -> Either c d<L2, R2>(f: (value: L) => L2, g: (value: R) => R2): Either<L2, R2>
Given two functions, maps the value inside `this` using the first if `this` is `Left` or using the second one if `this` is `Right`. If both functions return the same type consider using `Either#either` instead.
Left('Error').bimap(x => x + '!', x => x + 1)
Right(5).bimap(x => x + '!', x => x + 1)
Left('Error!')
Right(6)
map
Either a b ~> (b -> c) -> Either a c<R2>(f: (value: R) => R2): Either<L, R2>
Maps the `Right` value of `this`, acts like an identity if `this` is `Left`.
Left('Error').map(x => x + 1)
Right(5).map(x => x + 1)
Left('Error')
Right(6)
mapLeft
Either a b ~> (a -> c) -> Either c b<L2>(f: (value: L) => L2): Either<L2, R>
Maps the `Left` value of `this`, acts like an identity if `this` is `Right`.
Left('Error').mapLeft(x => x + '!')
Right(5).mapLeft(x => x + '!')
Left('Error!')
Right(5)
ap
Either a b ~> Either a (b -> c) -> Either a c<R2>(other: Either<L, (value: R) => R2>): Either<L, R2>
Applies a `Right` function over a `Right` value. Returns `Left` if either `this` or the function are `Left`.
Right(5).ap(Right(x => x + 1))
Right(5).ap(Left('Error'))
Left('Error').ap(Right(x => x + 1))
Left('Error').ap(Left('Function Error'))
Right(6)
Left('Error')
Left('Error')
Left('Function Error')
equals
Either a b ~> Either a b -> Bool(other: Either<L, R>): boolean
Compares `this` to another `Either`, returns false if the constructors or the values inside are different.
Left('Error').equals(Left('Error'))
Right(5).equals(Right(5))
Left(10).equals(Right(10))
Right(5).equals(Left('Error'))
true
true
false
false
chain
Either a b ~> (b -> Either a c) -> Either a c<R2>(f: (value: R) => Either<L, R2>): Either<L, R2>
Transforms `this` with a function that returns an `Either`. Useful for chaining many computations that may fail.
Left('Error').chain(x => Right(x + 1))
Right(5).chain(x => Right(x + 1))
Left('Error')
Right(6)
chainLeft
Either a b ~> (a -> Either c b) -> Either c b<L2>(f: (value: L) => Either<L2, R>): Either<L2, R>
The same as Either#chain but executes the transformation function only if the value is Left. Useful for recovering from errors.
Left('Error').chainLeft(x => Right(''))
Right(5).chainLeft(x => Right(999))
Right('')
Right(5)
join
Either a (Either a b) ~> Either a b<R2>(this: Either<L, Either<L, R2>>): Either<L, R2>
Flattens nested Eithers. `e.join()` is equivalent to `e.chain(x => x)`
Right(Right(5)).join()
Left(Left('Error')).join()
Right(5)
Left(Left('Error'))
alt
Either a b ~> Either a b -> Either a bother: Either<L, R>): Either<L, R>
Returns the first `Right` between `this` and another `Either` or the `Left` in the argument if both `this` and the argument are `Left`.
Left('Error').alt(Left('Error!'))
Left('Error').alt(Right(5))
Right(5).alt(Left('Error'))
Right(5).alt(Right(6))
Left('Error!')
Right(5)
Right(5)
Right(5)
reduce
Either a b ~> ((c, b) -> c, c) -> c<T>(reducer: (accumulator: T, value: R) => T, initialValue: T): T
Takes a reducer and an initial value and returns the initial value if `this` is `Left` or the result of applying the function to the initial value and the value inside `this`.
Right(5).reduce((acc, x) => x * acc, 2)
Left('Error').reduce((acc, x) => x * acc, 0)
10
0
extend
Either a b ~> (Either a b -> c) -> Either a c<R2>(f: (value: Either<L, R>) => R2): Either<L, R2>
Returns `this` if it's a `Left`, otherwise it returns the result of applying the function argument to `this` and wrapping it in a `Right`.
Left('Error').extend(x => x.isRight())
Right(5).extend(x => x.isRight())
Left('Error')
Right(true)
unsafeCoerce
(): R
Returns the value inside `this` or throws an error if `this` is a `Left`.
Right(5).unsafeCoerce()
Left('Error').unsafeCoerce()
5
// Error: Either got coerced to a Left
orDefault
Either a b ~> b -> b(defaultValue: R): R
Returns the value inside `this` if it's `Right` or a default value if `this` is `Left`.
Left('Error').orDefault(0)
Right(5).orDefault(0)
0
5
leftOrDefault
Either a b ~> a -> a(defaultValue: L): L
Returns the value inside `this` if it's `Left` or a default value if `this` is `Right`.
Left('Error').leftOrDefault('No error')
Right(5).leftOrDefault('No error')
'Error'
'No error'
orDefaultLazy
Either a b ~> (() -> b) -> b(defaultValue: R): R
Lazy version of `orDefault`. Takes a function that returns the default value, that function will be called only if `this` is `Left`.
Left('Error').orDefault(() => 0)
Right(5).orDefault(() => expensiveComputation())
0
5 // expensiveComputation is never called
leftOrDefaultLazy
Either a b ~> (() -> a) -> a(defaultValue: L): L
Lazy version of `leftOrDefault`. Takes a function that returns the default value, that function will be called only if `this` is `Right`.
Left('Error').leftOrDefault(() => 'No error')
Right(5).leftOrDefault(() => 'No error')
'Error'
'No error'
toMaybe
Either a b ~> Maybe b(): Maybe<R>
Constructs a `Just` with the value of `this` if it's `Right` or a `Nothing` if `this` is `Left`.
Left('Error').toMaybe()
Right(5).toMaybe()
Nothing
Just(5)
leftToMaybe
Either a b ~> Maybe a(): Maybe<L>
Constructs a `Just` with the value of `this` if it's `Left` or a `Nothing` if `this` is `Right`.
Left('Error').leftToMaybe()
Right(5).leftToMaybe()
Just('Error')
Nothing
ifLeft
(effect: (value: L) => any): this
Runs an effect if `this` is `Left`, returns `this` to make chaining other methods possible.
Left('Error').ifLeft((err) => console.log(err))
Right(5).ifLeft(() => console.log('Unexpected error'))
// Error
ifRight
(effect: (value: R) => any): this
Runs an effect if `this` is `Right`, returns `this` to make chaining other methods possible.
Left('Error').ifRight((result) => console.log(result))
Right(5).ifRight((result) => console.log(result))
// 5
either
(a -> c) -> (b -> c) -> Either a b -> c<T>(ifLeft: (value: L) => T, ifRight: (value: R) => T): T
Given two map functions, maps using the first if `this` is `Left` or using the second one if `this` is `Right`. If you want the functions to return different types depending on the either you may want to use `Either#bimap` instead.
Right(5).either(_ => 0, x => x + 1)
Left('Error').either(x => x + '!', _ => '')
6
'Error!'
extract
(): L | R
Extracts the value out of `this`.
Right(5).extract()
Left('Error').extract()
5
'Error'
swap
Either a b ~> Either b a(): Either<R, L>
Returns `Right` if `this` is `Left` and vice versa.
Right(5).swap()
Left(5).swap()
Left(5)
Right(5)