事件总线中回调参数的打字稿类型

时间:2020-09-29 14:01:31

标签: typescript event-bus

使用自制事件总线,我们会收到以下TS错误:

TS2345: Argument of type 'unknown' is not assignable to parameter of type 'AccountInfo | undefined'.
  Type 'unknown

事件总线使用类型unknown[]作为回调函数的参数。当然,不可能在事件总线的参数内设置每种可能的类型。因此,对于如何让TypeScript正确推断参数的类型或是否有其他解决方案,我们有些卡住了。

// eventBus.ts
type TCallBack = (...args: unknown[]) => boolean | void

const subscriptions: { [key: string]: TCallBack[] } = {}

interface ISubscription {
  eventName: string
  callback: TCallBack
}

export function unsubscribe({ eventName, callback }: ISubscription) {
  if (!subscriptions[eventName]) { return }
  const index = subscriptions[eventName].findIndex((l) => l === callback)
  if (index < 0) { return }
  subscriptions[eventName].splice(index, 1)
}

export function subscribe({ eventName, callback }: ISubscription) {
  if (!subscriptions[eventName]) { subscriptions[eventName] = [] }
  subscriptions[eventName].push(callback)
  return () => unsubscribe({ eventName, callback })
}

export function publish(eventName: string, ...args: unknown[]) {
  if (!subscriptions[eventName]) { return }
  for (const callback of subscriptions[eventName]) {
    const result = callback(...args)
    if (result === false) { break }
  }
}

在应用程序中的某个时刻,我们发布了login事件以触发所有订阅者:

// authServce.ts
publish('login', account)

之后触发订户:

// authStore.ts
export const setAccount = (account?: AuthenticationResult['account']) => {
  if (account) state.account = account
  else state.account = defaultState().account
  console.log('setAccount: ', state.account)
}

subscribe({
  eventName: 'login',
  callback: (account) => {
    setAccount(account)
  },
})

此代码可以完美运行,但是能够解决TS错误会很好。

1 个答案:

答案 0 :(得分:1)

扩展any并将默认值设置为any(因此您不必每次都指定类型)

// eventBus.ts
type TCallBack<T extends any = any> = (...args: T[]) => boolean | void

const subscriptions: { [key: string]: TCallBack[] } = {}

interface ISubscription<T> {
  eventName: string
  callback: TCallBack<T>
}

export function unsubscribe<T>({ eventName, callback }: ISubscription<T>) {
  if (!subscriptions[eventName]) { return }
  const index = subscriptions[eventName].findIndex((l) => l === callback)
  if (index < 0) { return }
  subscriptions[eventName].splice(index, 1)
}

export function subscribe<T>({ eventName, callback }: ISubscription<T>) {
  if (!subscriptions[eventName]) { subscriptions[eventName] = [] }
  subscriptions[eventName].push(callback)
  return () => unsubscribe({ eventName, callback })
}

export function publish<T>(eventName: string, ...args: T[]) {
  if (!subscriptions[eventName]) { return }
  for (const callback of subscriptions[eventName]) {
    const result = callback(...args)
    if (result === false) { break }
  }
}