How to delete object keys based on their values in TypeScript

I have an object that contains keys paired with functions as their values. My goal is to create a function where only the keys can be assigned if the corresponding value includes a callback function as its last parameter.

The callback function must take only one argument and return void.

interface Events {
  'valid0': (data: string, cb: () => void) => void, // valid
  'valid1': (data: number, cb: (data: string) => void) => void, // valid
  'invalid0': (data: string, cb: () => string) => void, // invalid return type of callback
  'invalid1': (data: string, cb: (string: string, number: number) => void) => void, // invalid number of callback arguments
}

type EventsWithCallback<E> = ???

type testFunction<Events> = (EventName: EventsWithCallback<Events>) => void

I am struggling to define the EventsWithCallback type. The error I encounter is:

Type 'T[P]' does not satisfy the constraint '(...args: any[]) => void'
. It does make sense in a way. I attempted to type T as
Record<string, (...args: any) => void>
but then it matches all strings.

type Last<T extends any[]> = T extends [...any, infer Last] ? Last : any;
type EventsWithCallback<T> = keyof { [P in keyof T as Last<Parameters<T[P]>> extends Function ? P : never]: T[P] };

In addition, extends Function matches any function as well as any type.

Thank you for any assistance. I hope the issue is clear.

Answer №1

Is this solution effective? Link to Experiment

interface Events {
  'valid0': (data: string, cb: () => void) => void, // valid
  'valid1': (data: number, cb: (data: string) => void) => void, // valid
  'invalid0': (data: string, cb: () => string) => void, // invalid return type of callback
  'invalid1': (data: string, cb: (string: string, number: number) => void) => void, // invalid number of callback arguments
}

type Last<T extends any[]> = T extends [...any, infer Last] ? Last : any

type EventsWithCallback<T> = { 
  [P in keyof T]: 
    T[P] extends (...args: infer A) => void // Check if it is a function
      ? Last<A> extends (arg: any) => any // Check if the last parameter is a function with a single parameter
        ? ReturnType<Last<A>> extends void // Check if it returns void
          ? P // return the parameter name
          : never
        : never
      : never
}[keyof T]

const testMethod = (eventName: EventsWithCallback<Events>) => {
  eventName = 'invalid0' // error
  eventName = 'invalid1' // error
  eventName = 'valid0'   // no error
  eventName = 'valid1'   // no error
}

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

How can I dynamically add a named property to a class during runtime and ensure that TypeScript recognizes it?

Within my coding project, I have implemented a decorator that alters a class by adding additional methods to it, specifically in the class A. However, when utilizing an instance of this class, the added methods do not show up in the autocomplete feature. A ...

Broadening Cypress.config by incorporating custom attributes using Typescript

I'm attempting to customize my Cypress configuration by including a new property using the following method: Cypress.Commands.overwrite('getUser', (originalFn: any) => { const overwriteOptions = { accountPath: `accounts/${opti ...

Remove an item from an array within Express using Mongoose

{ "_id": "608c3d353f94ae40aff1dec4", "userId": "608425c08a3f8db8845bee84", "experiences": [ { "designation": "Manager", "_id": "609197056bd0ea09eee94 ...

Error message in Angular when promises are not defined

Recently, I started working with promises for the first time. I have a function that returns a promise: public DatesGenerator(futercampaign: ICampaign, searchparam: any, i: number): ng.IPromise<any> { return this.$q((resolve, reject) => { ...

Achieving Jest integration with Angular 9 in a Storybook setup

We are currently utilizing Storybook 5 alongside Angular 9, with Jest 26 for some of the testing procedures. The issue we're facing arises when using Typescript version below 3.8.0 - a requirement for Angular 9's ng build --prod. This results in ...

The filter function fails to work on undefined values

My eyes have been fixed on this code for quite some time now... I need assistance I am currently working with a filter and trying to eliminate undefined objects from an array: .then((items) => { const filtered = items.map( i => { if (i ...

The JSX element with type 'Component' does not support any construct or call signatures at this time

I am facing an issue with the following code snippet const pageArea = (Component:ReactNode,title:string) => ({ ...props }) => { return ( <> <div> { Component && (<Component { ...

Encountering issues with Sequelize Typescript on delete cascade functionality

One issue I encountered is related to the FK relationship between Group and GroupAttendee. Whenever I try to call Group.destroy(), a foreign key constraint failure exception regarding the GroupAttendee entries pops up. I know how these constraints work at ...

The integration of react-color Saturation with @types/react-color is currently unavailable

In my quest to develop a customized color picker, I am utilizing the react-color library (^2.19.3) together with @types/react-color (^3.0.4). The issue arises when trying to import the Saturation component since it is not exported from the types in the ind ...

What is the process for consolidating a TypeScript web application with node modules into a single JavaScript file using the command line

How can I efficiently set up a TypeScript web application with all node modules compiled into one JavaScript file for use in HTML? If my project structure looks like this: ├── node_modules │ └── mathjs │ └── (dependencies, etc.) ...

Exploring Blob functionality in TypeScript?

I defined a global Blob object: declare global { interface Blob { prototype: Blob; new (name: string, url: string): Blob; } } It is functioning correctly in this code snippet: export const blobToFile = (blob: Blob) => { let file: File | n ...

Is it possible to combine the values of parents in an array of deeply nested objects?

I have an array containing nested objects that need their id property updated by combining all parent names. The id should be the current node's name value joined with its parents' names, separated by '/'. treeData = [{ name: ...

Generating Angular2 CLI components with Angular-Meteor integration

Exploring Angular2 CLI and Meteor has been an interesting journey for me. One thing I've noticed is that when I create a component using Angular2 CLI, integrating it into another module is as simple as including it in the declarations array of that mo ...

Creating a JSON file using an object to send requests in Angular

In my Angular 7 project, I am trying to send a multipart request to the server that includes a file (document_example.pdf) and a json file containing a data object (data_object_example.json). The content of data_object_example.json is as follows: { " ...

When the user clicks on a specific element, ensure that it is the main focus and generate an overlay

One of my challenges is implementing a custom element that captures user input upon clicking, focusing on it and overlaying other elements. I want the overlay to disappear if the user clicks outside the div. I attempted to achieve this using the iron-over ...

Using Lerna with Docker for Next.js and GraphQL applications

Currently, I am working with lerna and everything runs smoothly locally. However, when I attempt to build the image and operate it through Docker, it does not function as expected. FROM node:16-alpine3.11 ENV NODE_ENV=production COPY . /app WORKDIR /app R ...

Angular application triggering multiple subscribe method calls upon a link click event

Here is the code for my navbar component: <li *ngFor="let item of menu"> <a *ngSwitchCase="'link'" routerLinkActive="active" [routerLink]="item.routerLink" (click)="Navigation(item.title)&q ...

Avoid using the --transpileOnly option as it may cause issues, and refrain from using the --ignoreWatch option as

Having some issues running a script on my node server using ts-node-dev with the parameters --transpileOnly and --ignoreWatch. Unfortunately, I keep encountering errors: https://i.sstatic.net/8HxlK.png Here is a snippet from my package.json: https://i.s ...

Issue with demonstrating the Material Angular expansion-panel feature

Exploring the world of Material Angular has been an exciting journey. Recently, I've been experimenting with adding components to my project. Currently, I'm focused on incorporating the expansion-panel component example into a dialog. However, de ...

What is the best way to access a property within a typescript object?

I'm working with the following code snippet: handleSubmit() { let search = new ProductSearch(); search = this.profileForm.value; console.log(search); console.log(search.code); } When I run the console.log(search) line, it outputs: ...