验证 Iterator 与 AsyncIterator 类型

Verify Iterator versus AsyncIterator type

JavaScript 中是否有任何已知的技巧可以在不触发迭代的情况下区分 IteratorAsyncIterator

我正在尝试实现以下类型检查器:

function isAsyncIterator<T>(i: Iterator<T> | AsyncIterator<T>): boolean {
    // returns:
    //  - true, if i is an asycnronous iterator
    //  - false, if i is a syncronous iterator
}

我知道调用 next 会这样告诉我们,但我在无法触发迭代时需要它。

此外,即使我在 TypeScript 中给出了示例,我也需要在 运行-time 严格检查它。

这是不可能的。 迭代器 是由协议定义的,而不是由有形的 属性 定义的,因此由于 halting problem one cannot determine whether an object is an iterator without actually iterating it. See also the similar question .

但是,有一些很好的启发式方法可以检测大多数行为良好的迭代器实现和所有内置迭代器:

  • 迭代器通常是可迭代的(它们不能在 for … of 循环中使用,否则会限制它们的用途)并且会为此委托给自己。所以

    function isIterator(obj) {
        if (Object(obj) !== obj) return false;
        const method = obj[Symbol.iterator];
        if (typeof method != 'function') return false;
        const iter = method.call(obj);
        return iter === obj;
    }
    function isAsyncIterator(obj) {
        if (Object(obj) !== obj) return false;
        const method = obj[Symbol.asyncIterator];
        if (typeof method != 'function') return false;
        const aIter = method.call(obj);
        return aIter === obj;
    }
    
  • 迭代器继承自内置 %IteratorPrototype%。在 iterator helpers proposal 引入全局 Iterator 构造函数之前,这些对象的访问和测试有点麻烦,但仍然有可能。

    const GeneratorPrototype = Object.getPrototypeOf(function*() {}).prototype;
    const IteratorPrototype = Object.getPrototypeOf(GeneratorPrototype);
    const AsyncGeneratorPrototype = Object.getPrototypeOf(async function*() {}).prototype;
    const AsyncIteratorPrototype = Object.getPrototypeOf(AsyncGeneratorPrototype);
    
    function isIterator(obj) {
        return IteratorPrototype.isPrototypeOf(obj);
    }
    function isAsyncIterator(obj) {
        return AsyncIteratorPrototype.isPrototypeOf(obj);
    }
    

    (当然,就像任何 instanceof 检查一样,这不适用于来自其他领域的对象,这就是我推荐第一种方法的原因)