Typescript generics for 2nd optional argument

Advertisements

Generics in typescript is pretty advanced for me. But I managed to got this working

export type Events = {
  LOGIN: undefined
  NAVIGATION: {
    screen: string
  }
  SUPPORT: {
    communication_method: 'chat' | 'email' | 'phone'
  }
}

export function trackEvent<K extends keyof Events>(eventName: K, eventValues: Events[K]) {
  if (Platform.OS === 'web') return
  logEvent(eventName, eventValues ?? {})
}

trackEvent('LOGIN', undefined) // no TS error
// is there a way to make this work with just trackEvent('LOGIN') 

trackEvent('SUPPORT') // 👍 TS error because missing 2nd argument
trackEvent('SUPPORT', { communication_method: 'chat' }) // TS helps writing this

But wondering if there is a way to make this work with just trackEvent('LOGIN') without error and trackEvent('SUPPORT') with error?

>Solution :

We can use a rest parameter. If the type of Events[K] is undefined, the type of the rest parameter evaluates to an empty tuple. The empty tuple forbids additional arguments from being passed to the function. It can be optionally replaced by [undefined?] if you still want to be able to provide undefined.

export function trackEvent<K extends keyof Events>(
  eventName: K, 
  ...[eventValues]: (Events[K] extends undefined ? [undefined?] : [Events[K]])
) {
  if (Platform.OS === 'web') return
  logEvent(eventName, eventValues ?? {})
}

trackEvent('LOGIN', undefined)
//                  ~~~~~~~~~ Expected 1 arguments, but got 2

trackEvent('SUPPORT')
//          ~~~~~~~ Expected 2 arguments, but got 1

trackEvent('LOGIN') // 👍
trackEvent('SUPPORT', { communication_method: 'chat' }) // 👍

Playground

Leave a Reply Cancel reply