2017-04-14 4 views
0

ユニオンタイプを使用して軽量イベントシステムを構築しようとしています。ユニオンタイプの1つのタイプのイベントをリスンすることができます。ここで私は(残念ながら、それは本当に種類を利用しない)、これまで持っているものです。ユニオンタイプの型保証イベントハンドラ

class EventSystem { 
    events: { [key: string]: { (event: EventType) }[] }; 

    constructor() { 
     this.events = {}; 
    } 

    emit(key: string, event: EventType): void { 
     var arr = this.events[key]; 
     for (let i = 0; i < arr.length; ++i) { 
      arr[i](event); 
     } 
    } 

    on(key: string, callback: (event: EventType) => void) { 
     if (key in this.events) { 
      this.events[key].push(callback); 
     } else { 
      this.events[key] = [callback]; 
     } 
    } 
} 

interface EventA { 
    foo: Number 
} 

interface EventB { 
    bar: Number 
    baz: string 
} 

type EventType = EventA | EventB 

const EventNames = { 
    EventA: 'EventA', 
    EventB: 'EventB' 
} 

let x = {foo: 2} as EventA; 
let y = { 
    bar: 4, 
    baz: "test" 
} as EventB; 


let es = new EventSystem(); 

es.on(EventNames.EventA, function (a: EventA) { 
    console.log(a); 
}); 

//Triggers the on above 
es.emit(EventNames.EventA, x); 

//Unfortunately, this also triggers the on above 
es.emit(EventNames.EventA, y); 

私が本当に欲しいのはこのようなものです:

let es = new EventSystem<EventType>(); 

es.on<EventA>(function (a) { 
    //a is inferred to be EventA 
    console.log(a); 
}); 

//Triggers the on above 
es.emit(x); 

//Will not trigger the on, since the type does not match 
es.emit(y); 

//Type error, since number is not in EventType 
es.emit(4); 

は、活字体で可能なこのようなものですか?そうでない場合は、私がやっていることよりも型保証されたアプローチがありますか?または、一般的に、このタイプの動作を取得するためのより良い方法ですか?

編集:今、私は次のことをやっているために

。それはEventSystemクラス(私は何百ものメッセージタイプがあります)に定型文をたくさん追加し、私の意見ではapiを少し醜いものにしていますが、少なくとも型安全性はあります。重複したコードの量は、より良い方法が必要であると私に思います。

class EventSystem { 
    events: {[P in EventNames]: { (event: EventType) }[]} = { 
     'EventA': [], 
     'EventB': [] 
    }; 

    emitEventA(event: EventA): void { 
     this.events['EventA'].forEach((eventFunc) => eventFunc(event)); 
    } 

    emitEventB(event: EventB): void { 
     this.events['EventB'].forEach((eventFunc) => eventFunc(event)); 
    } 

    onEventA(callback: (event: EventA) => void) { 
     this.events['EventA'].push(callback); 
    } 

    onEventB(callback: (event: EventB) => void) { 
     this.events['EventB'].push(callback); 
    } 
} 

interface EventA { 
    foo: Number 
} 

interface EventB { 
    bar: Number 
    baz: string 
} 

type EventType = EventA | EventB 
type EventNames = 'EventA' | 'EventB' 

let x = { foo: 2 } as EventA; 
let y = { 
    bar: 4, 
    baz: "test" 
} as EventB; 


let es = new EventSystem(); 

es.onEventA(function (a) { 
    console.log(a); 
}); 

//Triggers the on above 
es.emitEventA(x); 

//Correctly caught now 
es.emitEventA(y); 

答えて

0

はい、可能です。私のプロジェクトで動作するコードの例をいくつか示します:

class ProducerClass { 
    private config; 
    private logger; 
    constructor(config: Config, logger: Logger); 
    enqueue<T extends string, M extends object>(topic: T, message: M): Promise<boolean>; 
    connect(): void; 
} 

const producer = ProducerClass(config, logger); 

type Topic = 'someTask' | 'someOtherTask'; 

interface Message { 
    objectId: number; 
} 

await producer.enqueue<Topic, Message>('someTask', { objectId: id }); 
+0

なぜですか? 'インタフェースメッセージ{}'はそれを正確に行います。 –

+0

申し訳ありませんが、あなたが正しいです。私は私のコメントを削除します。 –

関連する問題