在monad中使用递归concat方法时的Typescript通用类型串联

时间:2018-06-22 12:38:58

标签: typescript generics concatenation monads

我实现了一个Result monad,几乎可以用:)

class Result<T> {
  private readonly value: T;
  private readonly error: Error;

  constructor(value?: T, error?: Error) {
    if (value) {
      this.value = value;
    }
    if (error) {
      this.error = error;
    }
  }

  static ok<T>(ok: T): Result<T> {
    return new Result<T>(ok);
  }

  static error<T>(err: Error): Result<T> {
    return new Result<T>(undefined, err);
  }

  isOk(): boolean {
    return this.value !== undefined;
  }

  isError(): boolean {
    return this.error !== undefined;
  }

  chain<U>(f: (t: T) => Result<U>): Result<U> {
    return this.isOk() ?
      f(this.value) :
      Result.error<U>(this.error);
  }

  map<U>(f: (t: T) => U): Result<U> {
    return this.isOk() ?
      Result.ok<U>(f(this.value)) :
      Result.error<U>(this.error);
  }

  concat<U>(aResult: Result<U>): Result<(T | U)[]> {
    return this.isOk() ?
      Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
      Result.error(this.error);
  }

  getError(): Error {
    return this.error;
  }

  getValue(): T {
    return this.value;
  }
}

这是您的用法:

 const r1 = Result.ok(76)
      .concat(Result.ok('ddd'))
      .concat(Result.ok(true))
      .map((i: (number | string | boolean)[]) => i);

在这里,我将三个结果连接起来:一个数字,一个字符串和一个布尔值。

之后,我想将它们映射并应用转换方法。

.map 方法应将键入的(数字|字符串|布尔值)[] 作为参数。

但是,我收到以下错误消息:enter image description here

我知道问题出在哪里,但我现在知道如何解决。

问题与 .concat 方法有关:

concat<U>(aResult: Result<U>): Result<(T | U)[]> {
        return this.isOk() ?
          Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
          Result.error(this.error);
      }

每次连接时都会调用此方法,如果您愿意,可以“递归”调用。

因此,在我的情况下,这是流程:

  1. 第一个连击- U 是字符串'ddd' T 76 数字。该方法返回(数字|字符串)[]

  2. 第二个连接- U 已经是(数字|字符串)[] ,并且需要一个布尔值。 Typescript而不是返回(number | string | boolean)[] ,而是返回(boolean |(number | string)[])[]

  3. >

我可以使concat方法起作用并接受(string | number | boolean)[]签名吗?

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

如果T已经是数组,则应返回(TElement | U)[]。当this具有数组类型的T时,您可以添加额外的重载:

concat<TElement, U>(this: Result<TElement[]>,  aResult: Result<U>): Result<(TElement | U)[]>
concat<U>(aResult: Result<U>): Result<(T | U)[]>
concat<U>(aResult: Result<U>): Result<(T | U)[]> {
  return this.isOk() ?
    Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
    Result.error(this.error);
}

或者您可以使用条件类型,该条件类型在调用站点将发挥相同的作用,但是您将需要在函数中进行类型声明,但是您可以减少以下重载:

concat<U>(aResult: Result<U>): Result<( (T extends (infer TElement)[] ? TElement : T ) | U)[]> {
  return this.isOk() ?
    Result.ok<(T | U)[]>(Array().concat(this.value, aResult.getValue())) :
    Result.error(this.error) as any;
}