8

我正在尝试创建 Set 的一个子类,因为我不能简单地从它扩展,我正在包装它的功能。

我正在尝试实现 Symbol.iterator 方法,但 Flow 没有。

这是我拥有的代码:

/* @flow */
class CSet<T> {
    _set: Set<T>;
    [Symbol.iterator](): Iterator<T> {
        return this._set[Symbol.iterator];
    }
}

var a: CSet = new CSet();
for(var b of a){

}

core.js:309:5,29: property @@iterator
Property not found in
test.js:2:7,10: CSet

test.js:4:2,6:2: 
computed property keys not supported

第二个错误没什么大不了的,因为我可以很容易地压制它。我想知道我是否只是一起做错了什么。

4

4 回答 4

5

因为 Flow 目前没有对符号的一般支持,所以它表示的方式Symbol.iterator是 hacky 并且基本上阻止了现在在用户空间中定义迭代器的能力(它的支持只在库定义中有效):(

具体来说,Flow 期望一个可迭代对象上有一个@@iterator属性(这当然不是一个有效的属性名称——但这是为了获得库定义的支持而进行的临时破解)。

因此,在获得适当的符号支持之前,您最好的解决方法是为该模块创建一个库定义,该模块使用此@@iterator属性来理解 Flow:

// RealModule.js
export class CSet {
    [Symbol.iterator]() {
        return this._set[Symbol.iterator];
    }
}

.

// RealModule.FlowLibDef.js
declare module RealModule {
    declare class CSet<T> {
        _set: Set<T>;
        @@iterator(): Iterator<T>;
    }
}
于 2015-12-01T15:27:19.987 回答
5
// @flow
class MyCollection<T> {
    /*:: @@iterator(): Iterator<T> { return ({}: any); } */

    // $FlowFixMe: computed property
    [Symbol.iterator](): Iterator<T> {
        // NOTE: this could just as easily return a different implementation
        return new MyIterator(this);
    }
}

class MyIterator<+T> {
    /*:: @@iterator(): Iterator<T> { return (this: any); } */

    // $FlowFixMe: computed property
    [Symbol.iterator](): Iterator<T> {
        return this;
    }

    next(): IteratorResult<T, void> {
        return { done: false, value: someT };
        // or return { done: true, value: undefined };
    }
}

for (const value of new MyCollection()) {
    console.log(value);
}

这样做的原因是流解释/*:: code */为好像它是源代码中的流代码,除了它在运行时被注释掉,因此实际上不会影响代码。

Flow 本质上知道该@@iterator方法,尽管它不是有效的 JavaScript,因此我们将其定义为存在,返回一个Iterator<T>,并返回一个适用于它的值(即一个空对象转换为any)。

然后,计算属性方法会被流程完全忽略,就好像它根本没有定义一样。从方法中实际返回一个有效值是至关重要Iterator<T>的,否则事情会在运行时中断。

于 2017-05-24T19:00:09.843 回答
1

我发现了一个技巧,可以让您将用户定义的类标记为可迭代的,而无需为整个类编写和维护并行 libdef。

关键是编写一个实现的专用超类[Symbol.iterator](),并为该超类提供一个 libdef:

// IterableBase.js
export default class IterableBase {
  [Symbol.iterator]() {
    return this._Symbol_iterator();
  }
}
// IterableBase.js.flow
// @flow
declare class IterableBase<T> {
  @@iterator(): Iterator<T>;
}
export default IterableBase;

现在您可以让您的自定义类扩展IterableBase并实现替代方法名称:

// RealModule.js
// @flow
import IterableBase from './IterableBase';

class CSet<T> extends IterableBase<T> {
  _Symbol_iterator(): Iterator<T> {
    ...
  }
}

这显然仍然是一种 hack,但它应该比其他方法更容易、更安全。

于 2017-01-08T01:41:00.690 回答
0

试试这个javascript代码

// module -- start

let map = new WeakMap();

class Foo {
  constructor(any) {
    map.set(this, new Set(any));
  }
  values() {
    return map.get(this).values();
  }
  [Symbol.iterator]() {
    return this.values();
  }
}

// module -- end


for(let item of new Foo([1,2,3])) {
  console.log(item);
}

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
  2. 沙盒
于 2015-08-11T13:28:28.207 回答