I'm trying to write an event bus function that will return a few fixed helper functions such as get, set and sub. However I want to optionally allow a user to register additional functions (I'm calling reducers) but my function instance only intellisenses get, set and sub.
type Tail<T extends any[]> = T extends [any, ...infer U] ? U : never
type EventBus<T> = {
get: () => T
set: (atom: T) => void
sub: (cb: (atom: T) => any) => number
}
type Reducers<T> = Record<string, (atom?: T, ...args: any[]) => T>
type ReducerCallbacks<T extends Record<string, (...args: any[]) => void>> = {
[P in keyof T]-?: (...args: Tail<Parameters<T[P]>>) => void
}
function PubSub<T>(
atom: T,
reducers?: Reducers<T>,
): EventBus<T> & ReducerCallbacks<typeof reducers> {
let _atom = atom
const _subscribers: ((atom: T) => any)[] = []
const _get = (): T => _atom
const _set = (atom: T) => ((_atom = atom), _subscribers.forEach(i => i(_atom)))
const _sub = (cb: (atom: T) => any) => _subscribers.push(cb)
// remove the first argument from all the reducer functions
const _reducers: ReducerCallbacks<typeof reducers> = reducers
? Object.entries(reducers).reduce((acc, [k, v]) => {
acc[k] = (...args:Tail<Parameters<typeof v>>[]) => _set(v(_atom, ...args))
return acc
}, {} as ReducerCallbacks<typeof reducers>)
: null
return { get: _get, set: _set, sub: _sub, ..._reducers }
}
const ps = PubSub<number>(0, {
inc: atom => atom + 1,
dec: atom => atom - 1,
reset: () => 0,
sum:(atom, d:number) => atom + d
})
// These aren't intellisensed
ps.reset()
ps.sum()
