Uecko_ERP/apps/server/src/common/domain/result.ts
2025-02-01 22:48:13 +01:00

79 lines
2.0 KiB
TypeScript

export class Result<T, E extends Error = Error> {
private readonly isSuccess: boolean;
private readonly _data?: T;
private readonly _error?: E;
private constructor(props: { isSuccess: boolean; error?: E; data?: T }) {
const { isSuccess, error, data } = props;
if (isSuccess && error) {
throw new Error(`InvalidOperation: A result cannot be successful and contain an error`);
}
if (!isSuccess && !error) {
throw new Error(`InvalidOperation: A failing result needs to contain an error message`);
}
this.isSuccess = isSuccess;
this._error = error;
this._data = data;
Object.freeze(this);
}
static ok<T>(data?: T): Result<T, never> {
return new Result<T, never>({ isSuccess: true, data });
}
static fail<E extends Error = Error>(error?: E): Result<never, E> {
return new Result<never, E>({ isSuccess: false, error });
}
static combine(results: Result<any, any>[]): Result<any, any> {
for (const result of results) {
if (result.isError()) {
return result;
}
}
return Result.ok<any>();
}
isOk(): boolean {
return this.isSuccess;
}
isError(): boolean {
return !this.isSuccess;
}
get data(): T {
if (!this.isSuccess) {
throw new Error("Cannot get value data from a failed result.");
}
return this._data as T;
}
get error(): E {
if (this.isSuccess) {
throw new Error("Cannot get error from a successful result.");
}
return this._error as E;
}
/**
* 🔹 `getOrElse(defaultValue: T): T`
* Si el `Result` es un `ok`, devuelve `data`, de lo contrario, devuelve `defaultValue`.
*/
getOrElse(defaultValue: any): T | any {
return this.isSuccess ? this.data : defaultValue;
}
/**
* 🔹 `match<R>(onOk: (data: T) => R, onError: (error: E) => R): R`
* Evalúa el `Result`: ejecuta `onOk()` si es `ok` o `onError()` si es `fail`.
*/
match<R>(onOk: (data: T) => R, onError: (error: E) => R): R {
return this.isSuccess ? onOk(this.data) : onError(this.error);
}
}