Skip to content

Instantly share code, notes, and snippets.

@ecwyne
Created July 14, 2022 15:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ecwyne/4cd30136a837dab936c35e55f963bc21 to your computer and use it in GitHub Desktop.
Save ecwyne/4cd30136a837dab936c35e55f963bc21 to your computer and use it in GitHub Desktop.
Typesafe Pusher (validated with Zod)
import PusherServer from 'pusher';
import { z, ZodSchema } from 'zod';
import PusherClient from 'pusher-js';
const client = new PusherClient('...', { cluster: '...' });
const server = new PusherServer({
appId: '...',
key: '...',
secret: '...',
cluster: '...',
});
type ObjKeyof<T> = T extends object ? keyof T : never;
type TypesafePusherSchema = Record<string, Record<string, ZodSchema>>;
interface TypesafePusherOptions<TSchema extends TypesafePusherSchema> {
schema: TSchema;
}
export class TypesafePusher<TSchema extends TypesafePusherSchema> {
constructor(private options: TypesafePusherOptions<TSchema>) {}
server = (pusher: PusherServer) => ({
trigger: <
TChannel extends ObjKeyof<TSchema>,
TEvent extends ObjKeyof<TSchema[TChannel]>,
>(
channel: TChannel,
event: TEvent,
data: z.infer<TSchema[TChannel][TEvent]>,
) => {
pusher.trigger(
channel,
event,
this.options.schema[channel]![event]!.parse(data),
);
},
});
client = (pusher: PusherClient) => ({
subscribe: <TChannel extends ObjKeyof<TSchema>>(
channelName: TChannel,
) => {
const pusherChannel = pusher.subscribe(channelName);
return {
bind: <TEvent extends ObjKeyof<TSchema[TChannel]>>(
eventName: TEvent,
callback: (
data: z.infer<TSchema[TChannel][TEvent]>,
) => void,
) => {
pusherChannel.bind(eventName, (data: any) =>
callback(
this.options.schema[channelName]![eventName]!.parse(
data,
),
),
);
},
};
},
});
}
const api = new TypesafePusher({
schema: {
myChannel: {
myEvent: z.object({ id: z.string() }),
myOtherEvent: z.object({ id: z.number() }),
},
},
});
api.server(server).trigger('myChannel', 'myEvent', { id: '2' });
api.client(client)
.subscribe('myChannel')
.bind('myEvent', data => console.log(data.id));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment