Limit the parameter of a TypeScript method decorator based on the method's type

Is it possible to generically restrict the argument of a method decorator based on the type of the method it is applied to?

I attempted to obtain the method's type that the decorator is applied to using

TypedPropertyDescriptor<Method>
. However, the type system did not raise any errors.

function MyCache<Method extends (...args: any[]) => Promise<any>>(keyFn: (...args: Parameters<Method>) => string) {
  return function (target: object, methodName: string, descriptor: TypedPropertyDescriptor<Method>) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: Parameters<Method>) {
      const cacheKey = keyFn(...args);
      return originalMethod?.apply(this, args);
    } as Method;
  };
}

class CounterService {
  // There should be an error because the function signature does not match the argument list of the method
  @MyCache((id: string) => `test-${id}`)
  async getCounter(config: { valid: boolean }, value: number): Promise<number> {
    return 0;
  }
}

Test it out here Typescript Playground

Answer №1

It is important to shift your perspective slightly. Instead of confining the argument within the decorator, focus on restricting the method to which the decorator is applied. This distinction arises from the sequence in which the decorator factory function call and the resulting decorator are validated against the target.

The initial adjustment entails eliminating the reliance on Parameters, as it interferes with inference based on the arguments supplied to MyCache. By modifying the type argument of TypedPropertyDescriptor, we can trigger the intended error:

function MyCache<A extends any[]>(
    keyFn: (...args: A) => string
) {
    return function (target: object, methodName: string, descriptor: TypedPropertyDescriptor<(...args: A) => Promise<any>>) {
        const originalMethod = descriptor.value;

        descriptor.value = function (...args: A) {
            const cacheKey = keyFn(...args);
            return originalMethod!.apply(this, args);
        };
    };
}

class CounterService {
    // Error
    @MyCache((id: string) => `test-${id}`)
    async getCounter(config: { valid: boolean }, value: number): Promise<number> {
        return 0;
    }

    // ok
    @MyCache((id: string) => `test-${id}`)
    async getCounter2(id: string): Promise<number> {
        return 0;
    }
}

Playground Link

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

Assuming control value accessor - redirecting attention

import { Component, Input, forwardRef, OnChanges } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ selector: 'formatted-currency-input', templateUrl: '../v ...

The type '(props: Props) => Element' cannot be assigned to the type 'FunctionComponent<FieldRenderProps<any, HTMLElement>>' in React-final-form

I'm fairly new to using TypeScript, and I am currently working on developing a signUp form with the help of React-Final-Form along with TypeScript. Here is the code snippet that describes my form: import React from "react"; import Button from "@mater ...

Empty array being returned by Mongoose after calling the populate() function

I've been struggling for the past 3-4 hours, banging my head against the wall and scouring countless articles here on StackOverflow, but I just can't seem to get my response to populate an array correctly. I'm working with Express.js, Typesc ...

When defining a stripe in TypeScript using process.env.STRIPE_SECRET_KEY, an error of "string | undefined" is encountered

Every time I attempt to create a new stripe object, I encounter the error message "Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string& ...

Unable to access component properties through react-redux

Context: A React Native application utilizing Redux for managing complexity. Versions: typescript v3.0.3 react-native v0.56.0 redux v4.0.0 @types/react-redux v6.0.9 @types/redux v3.6.0 Issue: The main JSX component in my app is unable to access proper ...

Utilize the authenticated page across various tests in Playwright for efficient testing

Starting out fresh with playwright and node.js frameworks Currently in the process of developing a framework using playwright with typescript. Everything was smooth sailing until I reached the point where I needed to run my tests sequentially on the same ...

Join the Observable in Angular2 Newsletter for the latest updates and tips

One of my functions stores the previous URL address. prevId () { let name, id, lat, lng; this.router.events .filter(event => event instanceof NavigationEnd) .subscribe(e => { console.log('prev:', this.previo ...

Tips for utilizing parameters within SQL Server

Hello everyone! I am new to SQL Server in Azure functions using Typescript. I am currently facing an issue while trying to update a row in the database using declared variables, particularly with VARCHAR types. Strangely, it works fine in the database tool ...

Issue with updating component

Currently facing an issue with a component that utilizes the changeDetection: ChangeDetectionStrategy.OnPush The component's logic is as follows: ngOnInit(){ this.serivce.something .subscribe( evt => { // Logic to update values of the ar ...

Incorporating numerous query parameters in Angular version 14

I am currently working on developing a multi-item filter feature for my application and I am faced with the challenge of sending multiple query parameters in the API request to retrieve filtered items. My main concern is whether there is a more efficient ...

Adjusting the interface of a third-party TypeScript library

I am currently working on modifying a third-party interface. I'm curious about why this particular code is successful: import { LoadableComponentMethods as OldLoadableComponentMethods } from '@loadable/component'; declare module "load ...

Unfortunately, the utilization of an import statement outside a module is restricted when working with Electron

Is there a solution to the well-known problem of encountering the error message "Cannot use import statement outside a module" when working with an Electron-React-Typescript application? //const { app, BrowserWindow } = require('electron'); impor ...

Ensuring that a service is completely initialized before Angular injects it into the system

When Angular starts, my service fetches documents and stores them in a Map<string, Document>. I use the HttpClient to retrieve these documents. Is there a way to postpone the creation of the service until all the documents have been fetched? In ot ...

Tips for storing the device token received from Firebase Cloud Messaging in an Ionic2 application

Using the FCM plugin for ionic2, I was able to successfully implement push notifications. For reference, you can check out the plugin here. I followed the steps outlined in this Github repository, and everything is working smoothly so far. Now, my next go ...

Check out the uploaded file preview on React Native Expo!

I'm attempting to display a preview of the file uploaded by the user, which could be in pdf, img, or doc format. I tried a method that previews the file using a specific URL, but what I really want is for it to only show the preview of the uploaded fi ...

The ES6 import feature conceals the TypeScript definition file

I currently have two definition files, foo.d.ts and bar.d.ts. // foo.d.ts interface IBaseInterface { // included stuff } // bar.d.ts interface IDerivedInterface extends IBaseInterface { // more additional stuff } Initially, everything was funct ...

I'm looking for a way to modify the Turkish characters and spaces in the names of JSON data objects. I plan to do this using WebApi

I am facing an issue with fetching data through an API. The JSON data format contains Turkish characters and spaces, causing problems when trying to display the data in a datatable. I have attempted to use the replace and parse functions, but so far, I hav ...

Toggling multiple ions simultaneously does not function independently

I encountered a problem while working on an ionic app. I needed to have individual control over toggle switches, but my current code toggles all switches at once whenever one switch is tapped. Is there a way to fix this and manage each toggle switch separa ...

Error: The jasmine framework is unable to locate the window object

Currently, I am testing a method that includes locking the orientation of the screen as one of its functionalities. However, when using Jasmine, I encountered an error at the following line: (<any>window).screen.orientation.lock('portrait&apos ...

Whenever signing in with Next Auth, the response consistently exhibits the values of "ok" being false and "status" being 302, even

I am currently using Next Auth with credentials to handle sign-ins. Below is the React sign-in function, which can be found at this link. signIn('credentials', { redirect: false, email: email, password: password, ...