79 lines
2.0 KiB
TypeScript
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);
|
|
}
|
|
}
|