Home > Net >  Chain functions with callbacks TypeScript
Chain functions with callbacks TypeScript

Time:11-27

After reading an article about Handling Failure by Vladimir Khorikov, I try to implement it using TypeScript.

The example code was written in C#, which I can understand only the idea, not the code.

Here is my implementation so far.

export class Result<T> {
  success: boolean;
  private error: string | null;
  private _value: T;

  get value(): T {
    if (!this.success) {
      throw new Error('Cannot get value of an error result.');
    }

    return this._value;
  }

  get errorMessage(): string {
    if (this.success || this.error === null) {
      throw new Error('Cannot get error message of a success result.');
    }

    return this.error;
  }

  get failure(): boolean {
    return !this.success;
  }

  private constructor(success: boolean, error: string | null, value?: T) {
    if (success && error != null) {
      throw new Error('Success result cannot have error message');
    }

    if (!success && (error === null || error.length === 0)) {
      throw new Error('Failed result must have error message');
    }

    this.success = success;
    this.error = error;
    this._value = value as T;
  }

  /**
   * Create failure result
   * @param errorMessage error message
   * @returns failureResult
   */
  static fail<T>(errorMessage: string): Result<T> {
    return new Result(false, errorMessage);
  }

  /**
   * Create success result
   * @param value
   * @returns successResult
   */
  static ok<T>(value?: T): Result<T> {
    return new Result(true, null, value);
  }

  /**
   * Combine multiple results into one result
   * @param results
   * @returns result
   */
  static combine(...results: Result<any>[]): Result<any> {
    for (let result of results) {
      if (result.failure) {
        return result;
      }
    }

    return Result.ok();
  }

  /**
   * Do things on success result
   * @param func
   * @returns result
   */
  onSuccess(func: Function): Result<T> {
    if (this.success) {
      func();
    }

    return this;
  }

  /**
   * Do things on failure result
   * @param func
   * @returns result
   */
  onFailure(func: Function): Result<T> {
    if (this.failure) {
      func();
    }

    return this;
  }

  /**
   * Do things on both success and failure result
   * @param func
   * @returns result
   */
  onBoth(func: Function): Result<T> {
    func();

    return this;
  }
}

I am struggling to implement the chain functions.

In the example code, the onSuccess, onFailure, and onBoth functions are chained beautifully.

return Result.Combine(billingInfoResult, customerNameResult)
        .OnSuccess(() => _paymentGateway.ChargeCommission(billingInfoResult.Value))
        .OnSuccess(() => new Customer(customerNameResult.Value))
        .OnSuccess(
            customer => _repository.Save(customer)
                .OnFailure(() => _paymentGateway.RollbackLastTransaction())
        )
        .OnSuccess(() => _emailSender.SendGreetings(customerNameResult.Value))
        .OnBoth(result => Log(result))
        .OnBoth(result => CreateResponseMessage(result));

But in my code, it can only trigger void function. The function can't pass its result to the next function.

Could you show me how to implement this? Any suggestions would be appreciated.

CodePudding user response:

You seem to be implementing it differently?

Original Code shows:

public static Result OnSuccess(this Result result, Func<Result> func)
{
    if (result.Failure)
        return result;

    return func();
}

which would translate to:

onSuccess(fn: () => Result): Result {
    if (this.failure)
        return this;

    return fn();
}

but you have

onSuccess(func: Function): Result<T> {
    if (this.success) {
      func();
    }

    return this;
}

CodePudding user response:

Following the idea of the article that was suggested by Invizi, I have come up with this solution.

 onSuccess<U>(fn: (value: T) => U): Result<U> {
    if (this.success) {
      return Result.ok(fn(this._value))
    }

    return Result.fail(this.errorMessage)
  }

This makes the chain flexible with type just like the example code.

Result.combine(Rectangle.from(5, 5), Rectangle.from(1, 1))
  .onSuccess(() => {return new User('user')}) // Create new user
  .onSuccess(user => console.log(user.name)) // Print user name
  • Related