2015-08-11 16 views
5

Próbuję utworzyć podklasę Seta, a ponieważ nie mogę po prostu rozszerzyć go, zawijam jego funkcjonalność.Jak zaimplementować Symbol.iterator?

Próbuję zastosować metodę Symbol.iterator, ale Flow jej nie ma.

Jest to kod, który mam:

/* @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 

Drugi błąd nie jest tak ogromny z transakcji, ponieważ można łatwo tłumić go. Zastanawiam się, czy po prostu robię coś złego, mimo wszystko razem.

Odpowiedz

5

Ponieważ przepływu nie mają obecnie żadnego poparcia dla symboli, sposób reprezentuje Symbol.iterator jest hacky i w zasadzie pre daje możliwość definiowania iteratorów w przestrzeni użytkownika (jego obsługa działa tylko w definicjach bibliotek) :(

W szczególności Flow oczekuje, że iteracja ma na sobie właściwość @@iterator (co z pewnością nie jest poprawną nazwą właściwości - ale to był tymczasowym hackem, aby uzyskać wsparcie w definicjach bibliotek).

Więc dopóki ziemie właściwe podparcie symbol, najlepiej na obejście tutaj byłoby utworzyć definicję biblioteki dla tego modułu, który wykorzystuje tę właściwość do zrozumienia @@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>; 
    } 
} 
0

spróbuj tego kodu 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); 
} 

zobaczyć

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
  2. sandbox
+0

Mój zły, powinienem być bardziej szczegółowy. Dotyczy to prawidłowo sprawdzania typu Flow nie. Flow nie uważa, że ​​Set ma funkcję values ​​(). – Kyle

1

znalazłem trick, który pozwala oznaczyć klasę zdefiniowaną przez użytkownika jako iterable, bez konieczności pisania i utrzymywania równoległego libdef dla całej klasy.

Kluczem jest napisanie dedykowanego superklasę który implementuje [Symbol.iterator]() i zapewnić libdef tylko dla tego nadrzędnej:

// 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; 

Teraz możesz mieć swoją klasę zwyczaj rozszerzyć IterableBase i wdrożenie substytut nazwa metody:

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

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

To oczywiście nadal hack, b powinno to być łatwiejsze i bezpieczniejsze niż alternatywa.

2
// @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); 
} 

Powodem tego jest to, że działa interpretuje przepływu /*:: code */ jakby to było Kod Przepływ w źródle, z wyjątkiem, że jest wypowiedziało się w czasie wykonywania więc faktycznie nie wpływa na kod.

Przepływ wewnętrznie wie o sposobie @@iterator, mimo że nie jest ważny JavaScript, co określamy jako istniejący, zwrócenie Iterator<T> i powrocie wartość, która pracuje dla niego (tj pusty obiekt do roli any).

Obliczona właściwość metoda jest następnie całkowicie ignorowana przez przepływ, tak jakby nie była wcale zdefiniowana. Ważne jest, abyś zwrócił prawidłową metodę Iterator<T>, w przeciwnym razie sytuacja ulegnie zerwaniu w czasie wykonywania.