Skip to content

Either

Handling Exceptions With Either "Left" Or "Right"

either.ts

ts
export class Left<T> {
  readonly error: T;

  private constructor(error: T) {
    this.error = error;
  }

  isLeft(): this is Left<T> {
    return true;
  }

  isRight(): this is Right<never> {
    return false;
  }

  static create<U>(error: U): Left<U> {
    return new Left(error);
  }
}

export class Right<T> {
  readonly value: T;

  private constructor(value: T) {
    this.value = value;
  }

  isLeft(): this is Left<never> {
    return false;
  }

  isRight(): this is Right<T> {
    return true;
  }

  static create<U>(value: U): Right<U> {
    return new Right(value);
  }
}

export type Either<T, U> = Left<T> | Right<U>;

either.test.ts

ts
type JsonPrimitive = string | number | boolean | null | undefined;
type JsonObject = { [key: string]: JsonValue };
type JsonValue = JsonPrimitive | JsonValue[] | JsonObject;

const evaluate = (value: JsonValue): Either<Error, JsonValue> => {
  if (typeof value === "object" && value !== null) {
    if (Array.isArray(value) && value.length === 0) {
      return Left.create(new Error("Empty Array"));
    }

    if (Object.values(value).length === 0) {
      return Left.create(new Error("Empty Object"));
    }
  }

  if (!value) return Left.create(new Error("Value Is Falsy"));
  return Right.create(value);
};

test("it should returns left error", () => {
  const result = evaluate({});
  const error = new Error("Empty Object");

  expect(result.isLeft()).toBe(true);
  expect((result as Left<Error>).error).toEqual(error);
});

test("it should returns right value", () => {
  const obj = { message: "Hi!" };
  const result = evaluate(obj);

  expect(result.isRight()).toBe(true);
  expect((result as Right<JsonValue>).value).toEqual(obj);
});

Released under the MIT License.