不同 return 类型的 TypeScript 重载

TypeScript Overload with different return types

作为练习,我正在尝试将 Maybe 模式从类似 Haskell 的语言重新实现到 TypeScript 中。到目前为止一切顺利,我得到了一个令人满意的结果,而且它工作得很好,有一些我必须手动覆盖的类型检查内容。 我想更 JavaScript-ish 一点,我尝试定义一个函数映射,它可以 return 纯数据或数据承诺。

这里有一些代码更清楚:

class Maybe<A> {
  #value: A | null

  private constructor(value: A) {
    this.#value = value
  }

  static just<A>(value: A): Maybe<A> {
    return new Maybe(value)
  }

  static nothing<A>(): Maybe<A> {
    return <Maybe<A>>(<unknown>new Maybe(null))
  }

  static nullable<A>(value: A | null): Maybe<A> {
    if (value) {
      return this.just(value)
    } else {
      return this.nothing()
    }
  }

  value(): A {
    if (this.#value) {
      return this.#value
    } else {
      throw new Error('Maybe has no data')
    }
  }

  map<B>(mapper: (a: A) => Promise<B>): Promise<Maybe<B>> // I got an error here in the overloading
  map<B>(mapper: (a: A) => B): Maybe<B> {
    if (this.#value) {
      const result = mapper(this.#value)
      if (result instanceof Promise) {
        return result.then((e: B) => Maybe.just(e))
      } else {
        return new Maybe(mapper(this.#value))
      }
    } else {
      return Maybe.nothing()
    }
  }
}

我想知道是否可以像上面定义的那样定义 map 函数?

我觉得你可以这样做,看看评论

class Maybe<A> {
  #value: A | null

  // altered argument here
  private constructor(value: A | null) {
    this.#value = value
  }

  static just<A>(value: A): Maybe<A> {
    return new Maybe(value)
  }

  static nothing<A>(): Maybe<A> {
    // omitted casts
    return new Maybe<A>(null);
  }

  static nullable<A>(value: A | null): Maybe<A> {
    if (value) {
      return this.just(value)
    } else {
      return this.nothing()
    }
  }

  value(): A {
    if (this.#value) {
      return this.#value
    } else {
      throw new Error('Maybe has no data')
    }
  }

  // added two separate declarations
  map<B>(mapper: (a: A) => Promise<B>): Promise<Maybe<B>>;
  map<B>(mapper: (a: A) => B): Maybe<B>;

  // and single implementation which fits both declarations
  map<B>(mapper: (a: A) => B | Promise<B>): Maybe<B> | Promise<Maybe<B>> {
    if (this.#value) {
      const result = mapper(this.#value)
      if (result instanceof Promise) {
        return result.then((e: B) => Maybe.just(e))
      } else {
        return new Maybe(result)
      }
    } else {
      return Maybe.nothing()
    }
  }
}

所以现在 Maybe.just(0).map(x=>x+1)Maybe<number>Maybe.just(0).map(x=>Promise.resolve(x+1))Promise<Maybe<number>>

但是对于 Maybe.nothing().map(x=>Promise.resolve(x+1)) 你会收到 Maybe<number> 而接口会声明 Promies<Maybe<number>> 所以这不是一个好主意,因为你无法告诉函数 return 类型在运行时不调用它