let gen2:IterableIterator<string>;
function* gen1():Generator<string> {
yield* gen2;
}
收益*行是一个错误:"无法将迭代委派给值,因为其迭代器的'next'方法期望类型为'undefined',但包含生成器将始终发送'unknown'. ts(2766)"。
我是否漏掉了什么?这是有好的原因吗?
let gen2:IterableIterator<string>;
function* gen1():Generator<string> {
yield* gen2;
}
收益*行是一个错误:"无法将迭代委派给值,因为其迭代器的'next'方法期望类型为'undefined',但包含生成器将始终发送'unknown'. ts(2766)"。
我是否漏掉了什么?这是有好的原因吗?
这实际上是一个非常复杂的问题。我并不敢自认完全理解它。但也许我可以提供一些见解。
(在这个提交中) 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被引入来:
[...] 根据生成器的返回类型注释(即上面“Generator”中的TNext类型),正确检查并提供yield表达式的结果类型。
现在,如果您决定进行此类更改,则可能会破坏一些代码——目标是尽可能少地破坏。
我们必须注意,在生成器和非生成器迭代器中使用next()函数存在惯用差异。如ECMA-262备注所述为迭代器。
参数可以传递给next函数,但其解释和有效性取决于目标迭代器。for-of语句和其他常见的迭代器用户不传递任何参数,因此期望以这种方式使用的Iterator对象必须准备好处理调用而没有参数的情况。
迭代器主要用于for-of循环,它们不会向next传递参数。事实上,向next函数传递参数非常罕见(MDN甚至将其称为“零参数函数”)。因此,TNext默认值的唯一明智选项将是undefined。让它变成unknown将极大地阻碍类型检查(更不用说使用--strictNullChecks编译的代码了)。
如果使用生成器向next()函数传递参数不是一个相当常见的做法——它实际上有一个有效的用例……并在标准中定义了行为:
Generator.prototype.next(value)
next方法执行以下步骤:
- 让g成为this值。
- 返回?GeneratorResume(g, value, empty)。
要发送给生成器的值。
该值将被分配为yield表达式的结果。例如,在variable = yield expression中,传递给.next()函数的值将被分配给variable。
更不用说,在典型的用例中,第一个.next()调用将在没有参数的情况下调用,随后的调用将带有参数。不幸的是,没有办法指定“可选的第一次,未知的后续时间”类型,因此我想鉴于所有这些情况,他们对生成器中的TNext选择了unknown。
当然没有完美的情况。但他们必须为他们认为最少问题的解决方案做出选择。
所有这些问题都在此问题中讨论,供所有感兴趣的人参考。
: Generator<string>
并让TypeScript自己推断返回类型,那么它就是Generator<string, void, undefined>
。 - Patrick Roberts