2015-08-11 16 views
5

Sto provando a creare una sottoclasse di Set, e dal momento che non posso limitarmi a estenderlo, sto completando la sua funzionalità.Come implementare Symbol.iterator?

Sto cercando di implementare il metodo Symbol.iterator, ma Flow non ne ha.

Questo è il codice che ho:

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

Il secondo errore non è così grande di un affare dato che posso facilmente sopprimerla. Mi chiedo se sto facendo qualcosa di sbagliato tutto insieme però.

risposta

5

perché il flusso non ha attualmente un sostegno generale per i simboli, il modo in cui essa rappresenta Symbol.iterator è hacky e fondamentalmente pre sfoga la possibilità di definire iteratori in userspace per ora (il suo supporto funziona solo nelle definizioni di libreria) :(

Specificamente Flow si aspetta che un iterable abbia una proprietà @@iterator su di essi (che non è certamente un nome di proprietà valido - ma questo è stato un trucco temporaneo per ottenere supporto nelle definizioni delle librerie).

Così fino a quando le terre corretta simbolo di supporto, la soluzione migliore per risolvere il problema qui sarebbe quello di creare una definizione di libreria per questo modulo che utilizza questa proprietà @@iterator per la comprensione del flusso:

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

provare questo codice 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); 
} 

vedere

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

Il mio male, dovrei essere più specifico. Questo è per quanto riguarda il controllo del tipo non Flow correttamente. Flow non considera Set per avere una funzione values ​​(). – Kyle

1

ho trovato un trucco che consente di contrassegnare una classe definita dall'utente come iterabile, senza bisogno di scrivere e mantenere un libdef parallelo per l'intera classe.

La chiave è quello di scrivere una superclasse dedicato che implementa [Symbol.iterator](), e fornire un libdef solo per questo superclasse:

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

Ora si può avere la classe personalizzata estendere IterableBase e implementare il sostituto nome del metodo:

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

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

Questo è ovviamente ancora un hack, b così dovrebbe essere più facile e più sicuro dell'alternativa.

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); 
} 

Il motivo per cui funziona è che interpreta flusso /*:: code */ come se fosse codice flusso nell'origine, tranne che è commentata a runtime quindi in realtà non pregiudica il codice.

flusso conosce intrinsecamente sul metodo @@iterator, nonostante non sia valido JavaScript, così si definiscono come esistente, restituendo un Iterator<T>, e restituendo un valore che funziona per esso (cioè un cast oggetto vuoto come any).

Il metodo di proprietà calcolato viene quindi ignorato completamente dal flusso, come se non fosse affatto definito. È fondamentale restituire un metodo valido Iterator<T> dal metodo, altrimenti le cose si interromperanno in fase di esecuzione.