Utilizing Typescript generics with an optional second parameter

Learning about generics in typescript has been quite challenging for me. However, I was able to make it work successfully.

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

However, I am wondering if there is a method to have trackEvent('LOGIN') work without any errors and trackEvent('SUPPORT') trigger an error?

Answer №1

A handy technique is using a rest parameter. When the data type of Events[K] is undefined, the rest parameter's type translates to an empty tuple. This empty tuple restricts any extra arguments from being passed into the function. If you still want the option to pass undefined, you can consider using [undefined?].

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

trackEvent('LOGIN', undefined)
//                  ~~~~~~~~~ Expected 1 argument, but received 2

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

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

Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Eliminate the need to input the complete URL every time when making server calls

Currently, my springbok application has a React-Typescript frontend that is functioning well. I am using the request-promise library to make requests like this: get('http://localhost:8080/api/items/allItems', {json: true}). However, I would like ...

Are fp-ts and Jest the perfect pairing for testing Option and Either types with ease?

When working with fp-ts, and conducting unit tests using Jest, I often come across scenarios where I need to test nullable results, typically represented by Option or Either (usually in array find operations). What is the most efficient way to ensure that ...

Vue: Storing selected list values in an array

I am working on a Vue application where I need to select two elements from a list component and place them inside an array. Currently, I have my list set up with selection functionality thanks to Vuetify. I have bound the selected items to an array using v ...

Creating Mongoose models in TypeScript with multiple connections

Attempting to build a model with multiple connections as per the documentation, I encountered the following error: TS2345: Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of ty ...

After the "markerClick" event triggers in Angular2 SebmGoogleMapMarker, the view fails to update

I am dealing with an array structured like this: locations: marker[] = [ {id: '1', lat: 51.5239935252832, lng: 5.137663903579778, content: 'Kids Jungalow (5p)', iconUrl: 'img/marker.png'}, {id: '2&apos ...

Is it acceptable to manipulate the prevState parameter of the setState function as mutable?

It is commonly known that directly modifying this.state is not recommended, and instead setState should be used. Following this logic, I assumed that prevState should also be treated as immutable, and setState should always involve creating a new object i ...

Winston logs are unable to function within the Docker Container

I'm currently working on developing a nodejs/express app with typescript and have recently installed the winston package using npm install winston. I came across this helpful article that I've been following closely. Now, my goal is to dockerize ...

The 'books' property cannot be found on the 'client' type

I am currently integrating the Google Book API into my project and encountering an issue while trying to add a book to a library using gapi.client. The error I keep receiving is as follows: This is the request : gapi.client.books.mylibrary.bookshelves.volu ...

Utilizing getServerSideProps in the new app router (app/blah/page.tsx) for maximum functionality

I am a beginner in Next.js and I am currently experimenting with the new app router feature by placing my pages under app/.../page.tsx The code snippet provided works when using the page router (pages/blah.tsx) but encounters issues when used in app/blah/ ...

Incorporating a complex React (Typescript) component into an HTML page: A Step-by

I used to have an old website that was originally built with Vanilia Javascript. Now, I am in the process of converting it to React and encountering some issues. I am trying to render a compound React(Typescript) component on an HTML page, but unfortunatel ...

What is the best way to execute a function that retrieves data from a MySQL query and then sends it back as a result in Express.js?

How can I refactor my code to efficiently call a function that returns the result of a MySQL query and send it back in an Express.js response? I am attempting to streamline my SQL queries by exporting them into individual functions to eliminate duplicatio ...

Adding a baseURI to the image src in Angular 5 is causing issues with dynamically loading images

I am currently working on Angular 5.2.1 and I am facing an issue with loading an image from a server using its source URL. Here is the HTML code: <img #image [src]="cover" class="img-fluid" alt="NO image"> And here is the TypeScript code in image- ...

What is the proper way to define the Typescript type of the return value from the dispatch function in a Redux application?

fetchSomething is defined as follows: export const fetchSomething = createAsyncThunk( 'something', async () => { return Promise.resolve({name: 'jack'}) } ) This is the code within a React component: const dispatch = useApp ...

Can we verify if this API response is accurate?

I am currently delving into the world of API's and developing a basic response for users when they hit an endpoint on my express app. One question that has been lingering in my mind is what constitutes a proper API response – must it always be an o ...

Convert an object to nested JSON in Angular 5

I am struggling with using Angular 5 HttpClient to send a post request because I am having trouble casting an object to nested JSON. For instance, I have the following class: export class Team { members: Person[]; constructor(members: Person[]) ...

The callback function does not seem to work when using MUI v4 PropFunc

Encountering an issue with custom styling using PropFunc in Material UI (MUI) v4 (4.12.4). When providing a PropFunc in the traditional callback way to get CSS, it works as expected: const useStyles = makeStyles((theme) => { return { input: { ...

What is the best way to create a memoized function in React?

I am currently developing an application using react and typescript, and I am facing a challenge in memoizing a function. const formatData = ( data: number[], gradientFill?: CanvasGradient ): Chart.ChartData => ({ labels: ["a", ...

Eliminating an index from a JSON array using Typescript

I'm working with a JSON array/model that is structured as follows: var jsonArray = [0] [1] ... [x] [anotherArray][0] [1] ... [e] My goal is to extract only the arrays from [0] to [x] and save them into their ...

Do ES6 features get transpiled into ES5 when utilized in TypeScript?

After implementing ES6 features such as template strings, arrow functions, and destructuring in a TypeScript file, I compile the code to regular JavaScript... Does the TypeScript compiler also compile the ES6 syntax, or do I need to utilize another compil ...

Unable to add chosen elements to array - Angular material mat select allowing multiple selections

Can anyone assist me in figuring out what I am doing wrong when attempting to push data to an empty array? I am trying to only add selected values (i.e. those with checked as true), but I can't seem to get inside the loop This is the current conditi ...