Steps to create an instance method that only accepts the name of another instance method

I am looking to enhance an object by adding a method that specifically accepts the name of another method within the object. How can I achieve this in a way that dynamically narrows down the accepted names of methods, without hardcoding them?

Let's take the command method in the User class as an example. We want the command method to only accept the name of another method within the User class. How can we use keyof this to narrow down the acceptable types to only method names, excluding properties?

class User {
    nameFirst: string;
    nameLast: string;

    command(commandName: keyof this) {
        this[commandName].call(this);
    }

    sink() {}

    swim() {}
}

const alice = new User();
alice.command(`swim`); // Currently accepts `nameFirst` | `nameLast` | `command` | `sink` | `swim`, but we want it to accept only `sink` and `swim`

Answer №1

If you want to limit the acceptable types of the command function to only User method names without hardcoding them, you can achieve this by combining conditional and mapped types.

type FunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T];

class User {
  nameFirst: string;
  nameLast: string;

  command(commandName: FunctionPropertyNames<User>) {
    this[commandName].call(this);
  }

  sink() {
    console.log('Sink');
  }

  swim() {
    console.log('Swim');
  }
}

const alice = new User();
alice.command('swim'); // Now, only 'sink' and 'swim' can be passed as arguments

Answer №2

While TypeScript does not come with a predefined KeysMatching<T, V> type operator to extract keys of a type T whose values match another type V, there is a workaround that can be implemented to achieve this functionality for specific scenarios. Here is an example of how you can create your own KeysMatching<T, V> type:

type KeysMatching<T, V> = 
  { [K in keyof T]: T[K] extends V ? K : never }[keyof T];

Using this, you can define commandName as

KeysMatching<User, ()=>void>
to restrict it to zero-argument methods. However, be cautious of circularity warnings due to interdependency between types. To avoid this, omit "command" from the allowed keys using the Omit utility type:

class User {
  nameFirst: string = "";
  nameLast: string = "";

  command(commandName: KeysMatching<Omit<User, "command">, () => void>) {
    this[commandName].call(this);
  }

  sink() { }

  swim() { }

  needsParam(x: string) { console.log(x.toUpperCase()) }
}

const alice = new User();
alice.command("swim")
alice.command("sink");
alice.command("nameFirst"); // error, not accepted
alice.command("needsParam"); // error, not accepted

Visit the Playground link to test the code.

Answer №3

Are you looking for a solution to a specific problem? Maybe you could consider using this approach:

class Person {
    firstName: string;
    lastName: string;

    execute(action: keyof this) {
      if (typeof this[action] === 'function') {
            this[action].call(this);
      } 
      else {
        return
      }
    }

    eat() {}

    sleep() {}
}

const john = new Person();
john.execute(`eat`);

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

Tips for setting a default value in a Multi Select component with reactjs and Material UI

Is it possible to set a default value on a Multiple selection (CHIP) using reactjs and material ui? Despite searching extensively online, I have not been able to find any relevant documentation addressing this issue. import * as React from 'react&apos ...

What is the process for adding an item to an object?

I currently have the following data in my state: fbPages:{'123':'Teste','142':'Teste2'} However, I am in need of a dynamic solution like the one below: async getFbPages(){ var fbPages = {} awa ...

I am puzzled as to why my ajax script is giving me a 404 error even though the URL appears to be correct

Even though it's not a cross-domain problem, Ajax is returning a 404 error code. In my TIZEN Web application project, I am trying to make an ajax request to a WebService that contains functions necessary for the project. Initially, the xhr.status was ...

"Exploring the symbiotic relationship between Node.js and Express.js: an

I recently started learning Node.js and Express.js. I used the Express.js executable (express) to create an express application, which generated the following lines in app.js: ... var app = express(); http.createServer(app).listen(app.get('port' ...

Initiate the input change event manually

Struggling with creating a custom counter input component where the input value is controlled by custom increment/decrement buttons. Desired output: https://i.sstatic.net/oYl1g.png Content projection will be used to expose the input for form usage and a ...

Are multiple .then(..) clauses in Javascript promises better than just using one .then(..) clause?

In this particular scenario, I have set up a basic 'toy' node.js server that responds with the following JSON object: { "message" : "hello there" } This response is triggered by making a GET request to "http://localhost:3060/" So, it's reall ...

Whenever the user hits the "Enter" key, a new element will be generated and added to the bottom of an existing element

I am in need of creating a new element at the end of another element. For instance, let's say I have this element: <div id="elmtobetrig">Element that should be followed by another element when the user hits enter after this text. <! ...

Avoid triggering a second event: click versus changing the URL hash

One of the pages on my website has tabs that load dynamic content: HTML <ul> <li><a href="#tab-1">TAB 1</li> <li><a href="#tab-2">TAB 2</li> <li><a href="#tab-3">TAB 3</li> </ul&g ...

What occurs when an npm module is removed from the repository?

I came across an interesting article discussing how the deletion of a popular npm package (left-pad) by its author led to the breaking of various apps. I am puzzled by this situation. Doesn't an npm package's code get locally downloaded when you ...

If I type too quickly in the input field, it ends up displaying the incorrect div

I need to display two different dropdowns in my input field. To achieve this, I am using a method called shouldShowWarningBox that gets triggered every time the user updates the input and updates the value of showWarningBox accordingly. However, when I typ ...

How to implement a self-invoking function in React JS like you would in regular JavaScript?

Is it possible to invoke the function good without triggering it from an event? I want it to run as soon as the page loads, similar to a self-invoking JavaScript function. Check out this example import React from 'react'; class App extends Reac ...

Attempting to access a variable without wrapping it in a setTimeout function will

I have a form without any input and my goal is to automatically set the "responsible clerk" field to the currently logged-in user when the component mounts. Here's what I have: <b-form-select v-model="form.responsible_clerk" :op ...

Deselect the DOM element

Here is a jQuery code snippet: $(document).ready(function () { $(".story-area > h1, .story-area > p, .story-area > div > p").text(function () { return convertString($(this).text()); }); }); Additionally, there is a function de ...

Issue with AjaxComplete function failing to work

Currently, I am tackling the Opera workaround for printing IFrames. As we are aware, the only method to print an IFrame is by opening it in a new window and then printing it. However, I am encountering an issue where a series of ajax calls are triggered wh ...

ways to retrieve script template variable in angularjs application

I have written scripts to create a whiteboard using canvas inside the template page of AngularJS. Now I need to assign the values of the points[] variable to an AngularJS scope variable. <script> var points = []; </script> How can I access th ...

Perform a function on Angular 5

In my database, there is a table named settings. I have 2 backend endpoints: one for getting settings and another for updating settings. Now I am working on creating an Angular window to edit this table. I've set up an Angular Service to retrieve va ...

Is there a way to implement hover behavior for a Material-UI Button within a ButtonGroup component?

When using MUI v5, I am encountering an issue where the first button in the code provided is only half working. The button is initially colored red (both the border and text), however, upon hovering over it, the color of the border changes to blue. This is ...

In order to emphasize the chosen list item following a component refresh

SCENARIO: Let's consider a scenario where I have a component named list, responsible for displaying a list of all customers. Within this list, certain conditions are set up as follows: 1) Initially, the 1st list-item (e.g. Customer 1) is selected by ...

Having trouble populating the container with page content using AJAX, PHP, and JS?

Whenever I attempt to use the buttons to change the content, I keep receiving a 'There is no such page!' message. Below is the index.html: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- ...

Running complex operations within a sorting function just once

I am facing the challenge of sorting an array of objects based on multiple date fields, with the added complexity of excluding certain dates depending on the category. In order to optimize performance, I want to minimize the number of times the getUsefulJo ...