Skip to content

Instantly share code, notes, and snippets.

@yazaldefilimone
Created August 24, 2024 11:43
Show Gist options
  • Save yazaldefilimone/9986b4b2d168f885b155d37517cf80ab to your computer and use it in GitHub Desktop.
Save yazaldefilimone/9986b4b2d168f885b155d37517cf80ab to your computer and use it in GitHub Desktop.
class Result<T, E> {
private readonly value: T | null;
private readonly error: E | null;
private constructor(value: T | null, error: E | null) {
this.value = value;
this.error = error;
}
static ok<T, E = never>(value: T): Result<T, E> {
return new Result<T, E>(value, null);
}
static err<E, T = never>(error: E): Result<T, E> {
return new Result<T, E>(null, error);
}
isOk(): this is Result<T, never> {
return this.error === null;
}
isError(): this is Result<never, E> {
return this.error !== null;
}
unwrap(): T {
if (this.isOk()) {
return this.value as T;
}
throw new Error('Result thrown: called `unwrap()` on an `Err` value');
}
unwrapError(): E {
if (this.isError()) {
return this.error as E;
}
throw new Error('Result thrown: called `unwrapError()` on an `Ok` value');
}
unwrapOr(defaultValue: T): T {
return this.isOk() ? (this.value as T) : defaultValue;
}
map<U>(fn: (value: T) => U): Result<U, E> {
return this.isOk() ? Result.ok(fn(this.value as T)) : Result.err(this.error as E);
}
mapError<F>(fn: (error: E) => F): Result<T, F> {
return this.isOk() ? Result.ok(this.value as T) : Result.err(fn(this.error as E));
}
andThen<U>(fn: (value: T) => Result<U, E>): Result<U, E> {
return this.isOk() ? fn(this.value as T) : Result.err(this.error as E);
}
}
/// example usage
///
///
///
type NetworkError = { type: 'NetworkError'; message: string };
type ValidationError = { type: 'ValidationError'; field: string; message: string };
type AppError = NetworkError | ValidationError;
function divideNumbers(a: number, b: number): Result<number, string> {
if (b === 0) {
return Result.err('Division by zero');
}
return Result.ok(a / b);
}
function exampleUsage() {
const result = divideNumbers(10, 2)
.map((value) => value * 2)
.andThen((value) => (value > 10 ? Result.ok(`Result is ${value}`) : Result.err('Result is too small')));
if (result.isOk()) {
console.log(result.unwrap());
} else {
console.error(result.unwrapError());
}
}
function fetchUserData(userId: string): Result<{ name: string; age: number }, AppError> {
if (Math.random() < 0.5) {
return Result.err({ type: 'NetworkError', message: 'Failed to connect to server' });
}
if (userId === '') {
return Result.err({ type: 'ValidationError', field: 'userId', message: 'User ID cannot be empty' });
}
return Result.ok({ name: 'John Doe', age: 30 });
}
function handleUserData() {
const result = fetchUserData('123');
if (result.isOk()) {
const userData = result.unwrap();
console.log(`User: ${userData.name}, Age: ${userData.age}`);
return;
}
const error = result.unwrapError();
switch (error.type) {
case 'NetworkError':
console.error(`Network error: ${error.message}`);
break;
case 'ValidationError':
console.error(`Validation error in field ${error.field}: ${error.message}`);
break;
}
}
@yazaldefilimone
Copy link
Author

yazaldefilimone commented Feb 3, 2025

type ResultError<T> = [null, T];
type ResultOk<T> = [T, null];
export type Result<T, E extends Error = Error> = ResultOk<T> | ResultError<E>;

export const err = <E extends Error>(e: E | string): ResultError<E> => {
  if (typeof e === "string") {
    return [null, new Error(e) as E];
  }
  return [null, e];
};
export const ok = <T = void>(t?: NonNullable<T>): ResultOk<NonNullable<T>> => {
  return [(t ? t : true) as NonNullable<T>, null];
};

export const isOk = <T, E extends Error>(result: Result<T, E>): result is ResultOk<T> => {
  return result[1] === null;
};
export const isErr = <T, E extends Error>(result: Result<T, E>): result is ResultError<E> => {
  return result[1] !== null;
};

export function unwrap<T, E extends Error>(result: Result<T, E>): T {
  if (isOk(result)) {
    return result[0];
  }
  throw result[1];
}

export function unwrapError<T, E extends Error>(result: Result<T, E>): E {
  if (isErr(result)) {
    return result[1] as E;
  }
  throw new Error("result is not error");
}

@edilson258

Internally, I use something simpler, like... It's simpler but efficient for my use case.

e.g:

import { err, ok, type Result } from "@/shared/result";

 async request(x: ...): Promise<Result<void>> {
    const [value, error] = await call();
    if (error) return err(account_error);
	//...
    return ok();
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment