【问题标题】:Why are TypeScript's IterableIterator<> and Generator<> generics slightly different?为什么 TypeScript 的 IterableIterator<> 和 Generator<> 泛型略有不同?
【发布时间】:2020-02-22 09:20:43
【问题描述】:

在 TypeScript (3.6.3) 中,Generator 几乎与 IterableIterator 相同。当 Generator 扩展 Iterator 时,它默认第三个泛型参数 (TNext)unknownIterator 本身默认 TNextundefined。所以 GeneratorIterator(以及 IterableIterator)并没有像他们应该的那样排列。

let gen2:IterableIterator<string>;

function* gen1():Generator<string> {
    yield* gen2;
}

yield* 行是一个错误:“无法将迭代委托给 value,因为其迭代器的 'next' 方法需要类型 'undefined',但包含的生成器将始终发送 'unknown'.ts(2766)”。

我错过了什么吗?这样做有充分的理由吗?

【问题讨论】:

  • 如果 TypeScript github 问题中的某处埋藏了一个线程来解释这种差异,我不会感到惊讶。我自己也不知道原因,但我想指出,如果你删除 : Generator&lt;string&gt; 并让 TypeScript 自己推断返回类型,它就是 Generator&lt;string, void, undefined&gt;

标签: typescript iterator generator


【解决方案1】:

这实际上是一个非常复杂的问题。我并不假装完全理解它。但也许我可以提供一些见解。

他们为什么要添加它们?

(在this commit)。 Typescript 决定,无论好坏,它都希望对生成器进行更严格的类型检查。这实际上有一些正当的理由。举个例子

function* foo() {
    let m = 0;
  
    while (m < 10) {
      yield m++;
    }
    
    return "done";
}

let gen = foo(),
    curr;

while(!(curr = gen.next()).done) {}

// At his point we should know that 
// curr.value is a string because curr.done is true

这里我们可以看到问题——我们不知道一个值是按照我们应该的所有逻辑规则返回还是产生。所以他们介绍了TReturn。 TNext 是introduced 到:

[...] 根据生成器的返回类型注释的下一个类型(即上面Generator 定义中的TNext 类型),正确检查yield 表达式的结果并为其提供类型。

为什么是默认值?

现在,如果您决定进行此类更改,您可能会破坏一些代码 - 目标是尽可能少地破坏。

我们必须注意,在生成器和非生成器迭代器中使用next() 函数存在惯用差异。作为迭代器的ECMA-262 remarks

参数可以传递给next 函数,但它们的解释和有效性取决于目标迭代器。迭代器的 for-of 语句和其他普通用户不传递任何参数,因此期望以这种方式使用的迭代器对象必须准备好处理不带参数的调用。

迭代器主要用于不向 next 传递参数的 for-of 循​​环。实际上,将参数传递给下一个函数的情况很少见(MDN 甚至称其为“零参数函数”)。因此,默认值TNext 的唯一合理选项是undefined。将其设为unknown 将是类型检查的一大障碍(更不用说使用--strictNullChecks 编译的代码了)。

如果使用生成器将参数传递给 next() 函数不是很常见的做法,那就太好了——它实际上有一个有效的用例……并在 the standard 中定义了行为:

Generator.prototype.next()

next 方法执行以下步骤:

  1. gthis 值。
  2. 返回 ?GeneratorResume(g, value, empty)。

MDN:

要发送到生成器的值。

该值将作为yield 表达式的结果进行分配。例如,在variable = yield expression 中,传递给.next() 函数的值将被分配给variable

更不用说,在典型的用例中,第一个 .next() 调用将在没有参数的情况下被调用,随后的调用将被调用。不幸的是,没有办法指定“第一次可选,后续时间未知”类型,所以我想鉴于所有这些,他们在 Generators 中为TNext 选择了unknown

当然没有完美的情况。但他们必须满足于他们认为问题最少的事情。

所有这些问题都在this issue 中讨论,供任何感兴趣的人参考。

【讨论】:

    猜你喜欢
    • 2018-08-19
    • 2019-05-20
    • 2019-08-01
    • 2020-03-13
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多