在TypeScript(3.6.3)中, Generator <> 与 IterableIterator <> 几乎相同。当 Generator <> 扩展 Iterator <> 时,它将默认的第三个通用参数(TNext)设置为 unknown 。 迭代器<> 本身默认将 TNext 设置为未定义。因此, Generator 和 Iterator (以及 IterableIterator )的排列不尽如人意。
let gen2:IterableIterator<string>;
function* gen1():Generator<string> {
yield* gen2;
}
yield *行是一个错误:“无法将迭代委派给值,因为其迭代器的'next'方法期望类型为'undefined',但是包含的生成器将始终发送'unknown'。ts(2766)”。< / p>
我想念什么吗?有充分的理由吗?
答案 0 :(得分:6)
这实际上是一个非常复杂的问题。我不假装完全理解它。但是也许我可以提供一些见识。
(在此提交https://github.com/microsoft/TypeScript/commit/e8bf9584aa74aabfecb51a02edb13e3657508274#diff-5c3d60d8ed16d2857e28a35a61302b48中)。 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被介绍给:
[...]正确检查a的结果并提供类型 基于生成器返回类型的下一种类型的yield表达式 注释(即上面的Generator定义中的TNext类型)。 https://github.com/microsoft/TypeScript/pull/30790
现在,如果您决定进行此类更改,则可能会破坏一些代码-目标是尽可能减少破坏。
我们必须注意,在生成器和非生成器迭代器中,next()
函数的使用在习惯上有所不同。作为ECMA-262对迭代器的说明。
参数可以传递给下一个函数,但它们的解释 有效性取决于目标迭代器。的 语句和Iterators的其他普通用户不传递任何 参数,因此期望以这种方式使用的Iterator对象 必须准备好处理没有参数的调用。
迭代器主要用于for-of循环中,该循环不会将参数传递给next。实际上,很少将参数传递给下一个函数(MDN甚至将其称为“零参数函数”)。因此,TNext默认值的唯一明智的选择是undefined
。使其成为unknown
会大大阻碍类型检查(更不用说使用--strictNullChecks编译的代码了)。
如果不是很常见的做法是将参数与生成器一起传递给next()
函数,那就太好了-它实际上具有有效的用例...并且在标准中定义了行为:
25.4.1.2 Generator.prototype.next(value)
下一个方法执行以下步骤:
让g为this值。 返回? GeneratorResume(g,value)。
和MDN
要发送到生成器的值。 该值将作为yield表达式的结果分配。例如,在in变量= yield表达式中,将值传递给 .next()函数将分配给变量。
更不用说,在典型的用例中,第一个.next()调用将在没有参数的情况下被调用,随后的调用将被带参数。不幸的是,没有办法指定“可选的第一次,以后的未知时间”这种类型,因此,我想根据他们为Generators中的TNext为unknown
确定的所有条件。
当然,没有完美的情况。但是他们必须解决他们认为最没有问题的问题。
在本期https://github.com/Microsoft/TypeScript/issues/2983中讨论了所有这些问题,供有兴趣的人使用。