Skip to content

Instantly share code, notes, and snippets.

@revskill10
Forked from sidola/pubsub.ts
Created June 2, 2022 17:35
Show Gist options
  • Save revskill10/8c8d1c7603bacb0a0a9f1dd549e87c09 to your computer and use it in GitHub Desktop.
Save revskill10/8c8d1c7603bacb0a0a9f1dd549e87c09 to your computer and use it in GitHub Desktop.
Basic typesafe pub-sub implementation in Typescript
/* ------------------------------------------
Alternative impl. as a class can be found here: https://gist.github.com/sidola/eaf987d8c4c7e8445b61dc07c33a842f
Has a way smaller footprint and less typescript magic to grasp.
------------------------------------------ */
/**
* 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.
*
* Returns the given callback.
*/
type SubTypeFn<E> = <Key extends string & keyof E>(
event: Key,
fn: (message: E[Key]) => void
) => (message: E[Key]) => void
/**
* Defines the function type for the unsubscribe function.
*
* Extracts the keys from `E` as valid event types, and the matching
* property as the payload to the callback function.
*/
type UnsubTypeFn<E> = <Key extends string & keyof E>(
event: Key,
fn: (message: E[Key]) => void
) => void
/**
* Tie everything together.
*/
type PubSubType<E> = {
publish: PubTypeFn<E>
subscribe: SubTypeFn<E>
unsubscribe: UnsubTypeFn<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>()
* const subHandle = pubSub.subscribe('warn', (message) => {
* console.warn(message)
* })
*
* pubSub.publish('warn', { message: "Something bad happened!" })
* pubSub.unsubscribe('warn', subHandle)
*/
export function PubSub<E>(): PubSubType<E> {
// This any[] is our list of handlers functions. We don't have the
// necessary type information in here which is why it's any-typed.
const handlers: { [key: string]: any[] } = {}
return {
publish: (event, msg) => {
handlers[event].forEach(h => h(msg))
},
subscribe: (event, callback) => {
const list = handlers[event] ?? []
list.push(callback)
handlers[event] = list
return callback
},
unsubscribe: (event, callback) => {
let list = handlers[event] ?? []
list = list.filter(h => h !== callback)
handlers[event] = list
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment