@dancrumb/fpish
Version:
FP-friendly classes for Typescript
131 lines (130 loc) • 3.51 kB
JavaScript
import { Optional } from './Optional.js';
/**
* The `Either<R,L>` type represents 2 possible values of 2 possible types.
*
* A common use case it to use this to represent the outcome of a function that could
* be an error or a value: `const result: Either<ErrorType, ValueType>`. Convention has
* `ErrorType` as the left-value and `ValueType` as the right-value.
*
* The type will only ever be single-valued, but it will always be single-valued
*/
export class Either {
/**
* Create a Left-valued `Either`
* @param value
*/
static left(value) {
return new Either(Optional.of(value), Optional.empty());
}
/**
* Create a Right-valued `Either`
* @param value
*/
static right(value) {
return new Either(Optional.empty(), Optional.of(value));
}
left;
right;
constructor(l, r) {
this.left = l;
this.right = r;
}
/**
* Returns whether or not the value is left-valued
*/
isLeft() {
return this.left.isPresent();
}
/**
* Gets the left-value of the object.
*
* This will result in an error, if the object is not left-valued
*
* @throws {NoSuchElementException}
*/
getLeft() {
return this.left.get();
}
/**
* Returns whether or not the value is right-valued
*/
isRight() {
return this.right.isPresent();
}
/**
* Gets the right-value of the object.
*
* This will result in an error, if the object is not right-valued
*
* @throws {NoSuchElementException}
*/
getRight() {
return this.right.get();
}
map(lFunc, rFunc) {
return this.left.map(lFunc).orElseGet(() => this.right.map(rFunc).get());
}
mapLeft(lFunc) {
return new Either(this.left.map(lFunc), this.right);
}
mapRight(rFunc) {
return new Either(this.left, this.right.map(rFunc));
}
proceedLeft(lFunc) {
if (this.left.isPresent()) {
return lFunc(this.left.get());
}
return Either.right(this.right.get());
}
proceedRight(rFunc) {
if (this.right.isPresent()) {
return rFunc(this.right.get());
}
return Either.left(this.left.get());
}
/**
* Collapses an `Either` that may contain a left-value that is an `Either`
* @param either
*/
static joinLeft(either) {
if (either.isLeft()) {
return either.getLeft();
}
return Either.right(either.getRight());
}
/**
* Collapses an `Either` that may contain a right-value that is an `Either`
* @param either
*/
static joinRight(either) {
if (either.isRight()) {
return either.getRight();
}
return Either.left(either.getLeft());
}
/**
* Applies a function to the internal value. Since this returns void and takes functions that return
* void, this relies entirely on side-effects..
* @param lFunc
* @param rFunc
*/
apply(lFunc, rFunc) {
this.left.ifPresent(lFunc);
this.right.ifPresent(rFunc);
return this;
}
/**
* Runs the provided function if the left value is populated
*/
ifLeft(lFunc) {
this.left.ifPresent(lFunc);
return this;
}
/**
* Runs the provided function if the right value is populated
*/
ifRight(rFunc) {
this.right.ifPresent(rFunc);
return this;
}
}