【问题标题】:Typescript generic type concatenation when using a recursive concat method inside a monad在 monad 中使用递归 concat 方法时的 Typescript 泛型类型连接
【发布时间】:2018-06-22 12:38:58
【问题描述】:

我已经实现了一个几乎可以工作的 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 方法应该采用类型化的 (number | string | boolean)[] 作为参数。

但是,我收到以下错误消息:

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

问题在于 .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. 第一个 concat - U 是字符串 'ddd'T76 数字.该方法返回一个 (number | string)[]

  2. second concat - U 已经是 (number | string)[] 并且需要一个布尔值。 Typescript 不返回 (number | string | boolean)[],而是返回 (boolean | (number | string)[])[]

我可以让 concat 方法运行并接受 (string | number | boolean)[] 签名吗?

有什么想法吗?

【问题讨论】:

标签: typescript generics concatenation monads


【解决方案1】:

如果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;
}

【讨论】:

    猜你喜欢
    • 2021-10-09
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-22
    • 1970-01-01
    相关资源
    最近更新 更多