Created
October 1, 2025 17:24
-
-
Save b4lk0n/903fa38b92a2fd99bbe2de518c4398d7 to your computer and use it in GitHub Desktop.
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 { err, isErr, ok } from "./result"; | |
| function createError<const T extends readonly string[]>(_codes: T) { | |
| type Code = T[number]; | |
| class AppError<C extends Code = Code> extends Error { | |
| public readonly code: C; | |
| constructor(code: C) { | |
| super(`Error: ${code}`); | |
| this.code = code; | |
| } | |
| } | |
| return AppError as new <C extends Code = Code>( | |
| code: C, | |
| ) => AppError<C>; | |
| } | |
| // users module | |
| const UserError = createError(["already_exists", "banned"] as const); | |
| function getUser() { | |
| const rand = Math.random(); | |
| if (rand < 0.5) { | |
| return err(new UserError("already_exists")); | |
| } | |
| return ok("string"); | |
| } | |
| const res = getUser(); | |
| if (isErr(res)) { | |
| switch (res.err.code) { | |
| case "already_exists": | |
| console.log("User already exists"); | |
| break; | |
| // error, 'cause getUser does not return such error | |
| // case "banned": | |
| // console.log("You are banned"); | |
| // break; | |
| } | |
| } | |
| // profiles module | |
| const ProfileError = createError(["user_not_found", "account_banned"] as const); | |
| function geProfile() { | |
| const user = getUser(); | |
| if (isErr(user)) { | |
| switch (user.err.code) { | |
| case "already_exists": | |
| break; | |
| } | |
| return err(new ProfileError("user_not_found")); | |
| } | |
| return ok({ id: 10 }); | |
| } | |
| const profile = geProfile(); | |
| if (isErr(profile)) { | |
| switch (profile.err.code) { | |
| case "user_not_found": | |
| console.log("User not found"); | |
| break; | |
| // error, 'cause getProfile does not return such error | |
| // case "account_banned": | |
| // console.log("Account is banned"); | |
| // break; | |
| } | |
| } |
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
| export type Ok<T> = Readonly<{ | |
| tag: "Ok"; | |
| data: T; | |
| }>; | |
| export type Err<E> = Readonly<{ | |
| tag: "Err"; | |
| err: E; | |
| }>; | |
| export type Result<E, T> = Ok<T> | Err<E>; | |
| export function ok<T>(data: T): Ok<T> { | |
| return Object.freeze({ tag: "Ok", data }); | |
| } | |
| export function err<E>(err: E): Err<E> { | |
| return Object.freeze({ tag: "Err", err }); | |
| } | |
| export function isOk<E, T>(result: Result<E, T>): result is Ok<T> { | |
| return result.tag === "Ok"; | |
| } | |
| export function isErr<E, T>(result: Result<E, T>): result is Err<E> { | |
| return result.tag === "Err"; | |
| } | |
| export function isResult<E, T>(result: unknown): result is Result<E, T> { | |
| return ( | |
| result !== null && | |
| typeof result === "object" && | |
| "tag" in result && | |
| (result.tag === "Ok" || result.tag === "Err") | |
| ); | |
| } | |
| export function map<E, T, F>( | |
| result: Result<E, T>, | |
| fn: (data: T) => F, | |
| ): Result<E, F> { | |
| return result.tag === "Ok" ? ok(fn(result.data)) : result; | |
| } | |
| export function mapErr<E, T, F>( | |
| result: Result<E, T>, | |
| fn: (err: E) => F, | |
| ): Result<F, T> { | |
| return result.tag === "Err" ? err(fn(result.err)) : result; | |
| } | |
| export function unwrap<E, T>(result: Result<E, T>): T { | |
| if (result.tag === "Ok") { | |
| return result.data; | |
| } | |
| throw result.err; | |
| } | |
| export function unwrapOr<E, T>(result: Result<E, T>, defaultValue: T): T { | |
| if (isErr(result)) { | |
| return defaultValue; | |
| } | |
| return unwrap(result); | |
| } | |
| export function unwrapErr<E, T>(result: Result<E, T>): E { | |
| if (result.tag === "Err") { | |
| return result.err; | |
| } | |
| throw new Error("Result is ok"); | |
| } | |
| export function match<E, T, F, U>( | |
| result: Result<E, T>, | |
| onErr: (err: E) => F, | |
| onOk: (data: T) => U, | |
| ): F | U { | |
| return result.tag === "Err" ? onErr(result.err) : onOk(result.data); | |
| } | |
| export function invariant<E, T>( | |
| result: Result<E, T>, | |
| message: string, | |
| ): asserts result is Ok<T> { | |
| if (!isOk(result)) { | |
| const cause = unwrapErr(result); | |
| console.error(cause); | |
| throw new Error(message, { cause }); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment