Skip to content

Instantly share code, notes, and snippets.

@zephraph
Created January 24, 2024 02:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zephraph/104f9b711b203aa18a8dde3941073072 to your computer and use it in GitHub Desktop.
Save zephraph/104f9b711b203aa18a8dde3941073072 to your computer and use it in GitHub Desktop.
Rust Inspired Monads for TypeScript
const Never = undefined as never;
export class Result<T extends "ok" | "error", D, E extends Error> {
private constructor(
public readonly type: T,
public readonly data: T extends "ok" ? D : never = Never,
public readonly error: T extends "error" ? E : never = Never
) {}
static ok<T>(value: T): Result<"ok", T, never> {
return new Result("ok", value);
}
static err<E extends Error>(error: E): Result<"error", never, E> {
return new Result("error", Never, error);
}
static fromPromise<T>(promise: Promise<T>) {
return promise.then(Result.ok).catch(Result.err);
}
isOk(): this is { type: "ok"; data: T } {
return this.type === "ok";
}
isErr(): this is { type: "error"; error: E } {
return this.type === "error";
}
unwrap() {
if (this.isOk()) {
return this.data;
} else {
throw this.error;
}
}
unwrapErr() {
if (this.isErr()) {
return this.error;
} else {
throw new Error("Cannot unwrapErr an Ok result");
}
}
map<U>(fn: (value: T) => U) {
if (this.isOk()) {
return Result.ok(fn(this.data));
} else {
return Result.err(this.error);
}
}
mapErr<F extends Error>(fn: (error: E) => F) {
if (this.isErr()) {
return Result.err(fn(this.error));
} else {
return Result.ok(this.data);
}
}
}
export class Option<T extends "some" | "none", V> {
private constructor(
public readonly type: T,
public readonly value: T extends "some" ? V : never = Never
) {}
static some<T>(value: T): Option<"some", T> {
return new Option("some", value);
}
static none(): Option<"none", never> {
return new Option("none");
}
isSome(): this is { type: "some"; value: V } {
return this.type === "some";
}
isNone(): this is { type: "none" } {
return this.type === "none";
}
unwrap(): V {
if (this.isSome()) {
return this.value;
} else {
throw new Error("Cannot unwrap a None option");
}
}
map<U>(fn: (value: V) => U) {
if (this.isSome()) {
return Option.some(fn(this.value as V));
} else {
return Option.none();
}
}
}
import { Result, Option } from './rust-types';
import { match } from 'ts-pattern';
const exampleResult = Result.ok("success!")
const msg = match(exampleResult)
.with({ type: "ok" }, ({ data }) => data)
.with({ type: "error" }, ({ error }) => {
throw error;
})
.exhaustive();
console.log(msg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment