访问嵌套的通用类型 属性

Accessing generic type of nested property

我正在尝试在 TypeScript 中实现一个 monad;特别是 this example.

中的 join 方法

待解决问题如下:

const mm3 = new Maybe(new Maybe(3)); // Maybe<Maybe<number>>
const m3 = mm3.join();               // Maybe<number>

join 方法解除两个 Maybe 的嵌套。但是调用joinMaybe可以处于三种状态。预期输出如下所示。

new Maybe(new Maybe(3)).join(); // Maybe<number>(3)
new Maybe<number>(null).join(); // Maybe<number>(null)
new Maybe(3).join();            // 3 - Not the normal use-case for join()

模糊的实施尝试...

interface IMonad<T> {
    join(): T; // Works for cases #1 and #3 above but
               // really I want to express that we're
               // returning an IMonad<number> in the examples.
               // Or even better, a Maybe<number> in the case of Maybe 
}

class Maybe<T> implements IMonad<T> {

    private value: T | null;

    public constructor(value: T | null | undefined) {
        this.value = (value === null || value === undefined) ? null : value;
    }

    public join(): T {
        if (this.value === null) {
            return Maybe.of<??>(null); // 'number' in these examples
        } else {
            return this.value;
        }
    }

}

我正在努力思考这可能是如何工作的。有任何想法吗? TypeScript 有能力还是我需要 higher kinded types?

实现 Monad 的单个 实例 非常简单,例如 Maybe,只要您不想表示 Monad 本身,它确实需要 higher-kinded types... and TypeScript has no direct support for those, as you know. (It seems that some people have come up with 一些 间接 支持,但没有足够干净的东西我想在这里推荐)

如果你想强类型Maybe.prototype.join()而不用担心代码中任何叫做“Monad”的东西,我建议使用通用的this parameter,像这样:

class Maybe<T> {
  constructor(public value?: T) {}
  join<U>(this: Maybe<Maybe<U>>) { // this parameter
    return new Maybe(
      typeof this.value === "undefined" ? undefined : this.value.value
    );
  }
}

(我在这里只处理 undefined 作为 "nothing" 值;如果需要,您可以添加对 null 的支持)

this 参数要求在匹配类型的对象上将其作为方法调用。因此,通过让 join()this 成为某些通用 UMaybe<Maybe<U>>,我们保证 this.value 将(如果不是 undefined)本身具有一个 value 属性.

我们来测试一下:

const mm3 = new Maybe(new Maybe(3)); // Maybe<Maybe<number>>
const m3 = mm3.join(); // Maybe<number>

这正是您想要看到的。另请注意,调用 m3.join() 是一个编译时错误:

const oops = m3.join(); // error!
/* The 'this' context of type 'Maybe<number>' is not assignable to method's
  'this' of type 'Maybe<Maybe<unknown>>'.  Type 'number' is not assignable 
  to type 'Maybe<unknown>'. */

我认为这是正确的行为;你不能 join() 一个非嵌套的 monad。

好的,希望对您有所帮助;祝你好运!

Link to code