Skip to content

Instantly share code, notes, and snippets.

@adregan
Forked from duncan-rheaply/result.test.ts
Last active May 20, 2024 14:50
Show Gist options
  • Save adregan/1508255a5e84525958f9b4d80caffa39 to your computer and use it in GitHub Desktop.
Save adregan/1508255a5e84525958f9b4d80caffa39 to your computer and use it in GitHub Desktop.
Result
import { Result } from "./result";
describe("Result", () => {
describe("creating", () => {
describe("Result.ok()", () => {
test("it returns an ok result", () => {
const result = Result.ok("I'm good");
expect(result.isOk()).toBeTruthy();
});
test("an ok cannot be errored", () => {
const result = Result.ok("I'm not bad");
expect(result.isErr()).toBeFalsy();
});
});
describe("Result.err()", () => {
test("it returns an error result", () => {
const result = Result.err("I'm bad");
expect(result.isErr()).toBeTruthy();
});
test("an error cannot be ok", () => {
const result = Result.err("I'm not good");
expect(result.isOk()).toBeFalsy();
});
});
});
describe("result.match(onOk, onErr)", () => {
test("it can pattern match the ok value to the onOk op", () => {
const result: Result<string, Error> = Result.ok("hello");
const actual = result.match(
(s: string) => `${s.toUpperCase()}!!`,
(_) => 2,
);
expect(actual).toEqual("HELLO!!");
});
test("it can pattern match the err value to the onErr op", () => {
const result: Result<string, Error> = Result.err(Error("KABOOM"));
expect(
result.match(
(_) => "I have a bad feeling about this.",
(e: Error) => `Something went ${e.message}`,
),
).toEqual("Something went KABOOM");
});
});
describe("result.map(fn)", () => {
test("an ok value can be transformed via map", () => {
const result = Result.ok(2);
const value = result.map((x): string => `${x * 2}`);
expect(value).toEqual(4);
});
test("an err value cannot be transformed via map", () => {
const result = Result.err(Error("OH NO"));
const value = result.map((x) => x * 3);
expect(value).toEqual(null);
});
});
});
export class Result<Ok, Err = Error> {
#ok: Ok | null;
#err: Err | null;
private constructor({ ok, err }: { ok?: Ok; err?: Err }) {
this.#ok = ok ?? null;
this.#err = err ?? null;
}
static ok = <Ok>(ok: Ok): Result<Ok, never> => new Result({ ok });
static err = <Err>(err: Err): Result<never, Err> => new Result({ err });
isOk = (): this is Result<Ok, never> => this.#ok !== null;
isErr = (): this is Result<never, Err> => this.#err !== null;
match = <S, T>(
onOk: (ok: Ok) => S,
onErr: (err: Err) => T,
): this extends Result<Ok, never> ? S : T =>
//@ts-ignore
this.isOk() ? onOk(this.#ok) : onErr(this.#err);
map = <B>(fn: (a: Ok) => B): Result<B, never> | this =>
this.isOk() ? Result.ok(fn(this.#ok)) : this;
getOr = <V extends unknown>(value: V = null): Ok | V => (this.isOk() ? this.#ok : value);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment