Created
January 24, 2024 02:48
-
-
Save zephraph/104f9b711b203aa18a8dde3941073072 to your computer and use it in GitHub Desktop.
Rust Inspired Monads for TypeScript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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