具有不同返回类型的TypeScript重载

具有不同返回类型的TypeScript重载,typescript,monads,Typescript,Monads,作为练习,我试图将类似Haskell语言的模式重新实现为TypeScript。到目前为止,我得到了一个令人满意的结果,它工作得很好,有一些类型检查的东西,我必须手动覆盖。 我想更像一点JavaScript语言,我尝试定义一个函数映射,它可以返回纯数据或承诺数据 这里有一些代码需要更清楚地说明: class Maybe<A> { #value: A | null private constructor(value: A) { this.#value = value

作为练习,我试图将类似Haskell语言的
模式重新实现为TypeScript。到目前为止,我得到了一个令人满意的结果,它工作得很好,有一些类型检查的东西,我必须手动覆盖。
我想更像一点JavaScript语言,我尝试定义一个函数映射,它可以返回纯数据或承诺数据

这里有一些代码需要更清楚地说明:

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()
    }
  }
}
类{
#值:A | null
私有构造函数(值:A){
这个。#值=值
}
静态公正(值:A):也许{
返回新值(值)
}
静态无:可能{
返回(新的(空))
}
静态nullable(值:A | null):可能{
如果(值){
返回此值。just(值)
}否则{
还这个。没什么()
}
}
value():A{
如果(此值){
返回此值
}否则{
抛出新错误('可能没有数据')
}
}
map(mapper:(a:a)=>Promise):Promise//我在重载中遇到了一个错误
地图(制图员:(a:a)=>B):也许吧{
如果(此值){
常量结果=映射器(此值)
如果(承诺的结果实例){
返回结果。然后((e:B)=>Maybe.just(e))
}否则{
返回新值(映射器(此.#值))
}
}否则{
可能会回来
}
}
}

我想知道是否可以像上面定义的那样定义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()
    }
  }
}
类{
#值:A | null
//这里改变了论点
私有构造函数(值:A | null){
这个。#值=值
}
静态公正(值:A):也许{
返回新值(值)
}
静态无:可能{
//漏铸
返回newmaybe(null);
}
静态nullable(值:A | null):可能{
如果(值){
返回此值。just(值)
}否则{
还这个。没什么()
}
}
value():A{
如果(此值){
返回此值
}否则{
抛出新错误('可能没有数据')
}
}
//添加了两个单独的声明
地图(地图绘制者:(a:a)=>承诺):承诺;
地图(制图员:(a:a)=>B):可能;
//以及适合两种声明的单个实现
地图(地图绘制者:(a:a)=>B |承诺):也许|承诺{
如果(此值){
常量结果=映射器(此值)
如果(承诺的结果实例){
返回结果。然后((e:B)=>Maybe.just(e))
}否则{
返回新数据(结果)
}
}否则{
可能会回来
}
}
}
所以现在
Maybe.just(0).map(x=>x+1)
is
Maybe
但是
Maybe.just(0).map(x=>Promise.resolve(x+1))
is
Promise


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

什么错误?另外,考虑到B可能已经是承诺,为什么需要重载?
此重载签名与其实现签名不兼容。
因为当您处理异步和可能时,我发现最好提供一个
承诺
,而不是
可能
(这在代码中非常类似),但是第一个允许你使用wait和continue Maybe链,而不必处理
Maybe.map
中的承诺。嗯……是的,我明白你的意思。在我的第一个实现中,我得到了两个函数,一个是
map
,另一个是
asyncMap
,因此在第二种情况下,我总是有一个承诺。但是在这里,即使参数的类型不同,我们也不能在编译时决定调用两个不同的函数,对吗?不幸的是,我们不能。Typescript不提供上下文/类型感知转换