[英]How to implement the publish-subscribe pattern in TypeScript?
我正在为我的游戏创建一个事件系统,我的代码目前如下所示:
export const enum ET { Collision, Dying, Damage }
type ActionCallback = (scene: Scene, event: GameEvent) => void;
subscribe(eventType: ET, callback: ActionCallback) {
this.subscriptions[eventType].push(callback);
}
然后使用此函数的一些代码示例如下:
scene.events.subscribe(ET.Dying, handleEntityDeath);
handleEntityDeath = (scene: Scene, event: DyingEvent) => {
scene.deleteEntity(event.entity);
}
问题是 TypeScript 无法编译并说: event's type must be GameEvent and not DyingEvent
。
基本上,我需要一种“链接” ET.Dying
和DyingEvent
的方法,但我不知道该怎么做。 我想如果我能做到这一点,那么我可以像上面这样的事件处理程序,只有在第一个参数类似于ET.Dying
并且第二个参数是一个接受DyingEvent
的回调时,它才会编译。 如果回调有一个DamageEvent
参数,我希望它无法编译,如果这有意义的话。
可以做到这一点,如果可以,怎么做?
弄清楚了:
interface EventMap {
[ET.Collision]: CollisionEvent;
[ET.Dying]: DyingEvent;
// etc
}
subscribe = <T extends ET>(eventType: T, callback: (scene: Scene, event: EventMap[T]) => void) => {
this.subscriptions[eventType].push(callback);
}
// Example calling code below here:
scene.events.subscribe(ET.Dying, handleEntityDeath);
handleEntityDeath = (scene: Scene, event: DyingEvent) => {
scene.deleteEntity(event.entity);
}
编辑: TS Playground 示例
首先,创建一个新文件: PubSub.ts
:
/**
* Defines the function type of the publish function.
*
* Extracts the keys from `E` as valid event types, and the matching
* property as the payload.
*/
type PubTypeFn<E> = <Key extends string & keyof E>(
event: Key,
message: E[Key]
) => void
/**
* Defines the function type for the subscribe function.
*
* Extracts the keys from `E` as valid event types, and the matching
* property as the payload to the callback function.
*/
type SubTypeFn<E> = <Key extends string & keyof E>(
event: Key,
fn: MessageFn<E>
) => void
/**
* Defines the function type for the subscription callback. Ensures
* the message payload is a valid property of the event being used.
*/
type MessageFn<E> = <Key extends string & keyof E>(message: E[Key]) => void
/**
* Tie everything together.
*/
type PubSubType<E> = {
publish: PubTypeFn<E>,
subscribe: SubTypeFn<E>
unsubscribe: SubTypeFn<E>
}
/**
* Creates a new PubSub instance, the `E` type parameter should be a
* type enumerating all the available events and their payloads.
*
* @example
* type Events = {
* warn: { message: string },
* error: { message: string }
* }
*
* const pubSub = PubSub<Events>()
* pubSub.publish('warn', { message: "Something bad happened!" })
*/
export function PubSub<E>(): PubSubType<E> {
const handlers: { [key: string]: (MessageFn<E>)[] } = {}
return {
publish: (event, msg) => {
handlers[event].forEach(h => h(msg))
},
subscribe: (event, callback) => {
const list = handlers[event] ?? []
list.push(callback)
handlers[event] = list
},
unsubscribe: (event, callback) => {
let list = handlers[event] ?? []
list = list.filter(h => h !== callback)
handlers[event] = list
}
}
}
接下来,创建另一个文件,您将在其中使用PubSub
函数:
import { PubSub } from './PubSub'
type events = {
CreatedPerson: { id: string, name: string }
DeletedPerson: { personId: string; reason: string }
}
const pubSub = PubSub<events>()
pubSub.publish("CreatedPerson", { id: '1', name: 'cory' })
pubSub.subscribe("CreatedPerson", (message) => {
console.log(message)
})
参考:https ://gist.github.com/sidola/3b267f21c872e449ef4bbdae9e2baeab
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.