繁体   English   中英

如何在 TypeScript 中实现发布订阅模式?

[英]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.DyingDyingEvent的方法,但我不知道该怎么做。 我想如果我能做到这一点,那么我可以像上面这样的事件处理程序,只有在第一个参数类似于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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM