Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to method decorator that change the method signature #49229

Open
1 task
ZeeD opened this issue May 24, 2022 · 5 comments
Open
1 task

Support to method decorator that change the method signature #49229

ZeeD opened this issue May 24, 2022 · 5 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@ZeeD
Copy link

ZeeD commented May 24, 2022

Suggestion

As the title said, I just started using typescript and I want to have a method decorator that is able to change the method signature and have tsc knew about the new signature.

🔍 Search Terms

I have seen a lot of works around class decorators, like #4881 but nothing specifically on supporting the behavior I have described.

✅ Viability Checklist

My suggestion meets these guidelines:

  • [*] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [*] This wouldn't change the runtime behavior of existing JavaScript code
  • [*] This could be implemented without emitting different JS based on the types of the expressions
  • [*] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

If not for other things, I would like to understand if there is some way to set the types of decoratorto signal the new decorated function signature.

📃 Motivating Example

As a minimal example, look at

function decorator(_target: any, _propertyKey: string, descriptor: PropertyDescriptor): void {
    const method = descriptor.value;
    descriptor.value = function wrapper(a: number, b: number, c: number): void {
        // here you have access to a, b, c
        console.log(a, b, c);
        return method.call(this, a, b, c);
    }
}

class C {
    @decorator
    method(a: number, b: number): void {
        // here you have access to a, b
        console.log(a, b);
    }
}

new C().method(1, 2, 3); // here you pass a, b and c

tsc sasys that there is an error in the last line, because it doesn't "know" that C.method have a new signature, after applying the decorator.

💻 Use Cases

I'm implementing a simple method wrapper that add a parameter to do pre-checks on method invocation itself

@gund
Copy link

gund commented Jun 1, 2022

It is usually an anti-pattern as decorators are meant to be transparent - meaning that no changes to method signature must be done by the decorator and only a behavior may be added/changed (like doing validation/logging).

As for your case it seems like you are adding some extra "private" arguments that your consumer and receiver must not see/access and only your decorator can see and possibly some privileged part of the code?

In this case you can create a shortcut mapped type that adds new argument on the fly so you can cast your method in place just before invoking it. Pseudo TS could look something like this:

type AnyFunction = (...args: any[]) => any;

type ExtendedMethod<TMethod extends AnyFunction> = (
  // Add extra argument of type "string" at the end
  ...args: [...Parameters<TMethod>, string]
) => any;

type ExtendedObject<T> = {
  // Extend all methods on the object
  [P in keyof T]: T[P] extends AnyFunction ? ExtendedMethod<T[P]> : T[P];
};

function asExtended<T>(obj: T) {
  return obj as ExtendedObject<T>;
}

class Example {
  method(a: number, b: number) {}
}

// Standard call
new Example().method(1, 2);

// Extended call that knows about extra argument
asExtended(new Example()).method(1, 2, '3');

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Jun 2, 2022
@soffyo
Copy link

soffyo commented Oct 3, 2022

I don't agree that decorators should only be used for validation/logging. I think that adding methods and properties to classes via decorators is one of their main goals and capabilities and typescript should be able to access to the added signatures. If there is such wish to let decorators remain a 'hidden' thing, maybe the best option would be to have a flag in compiler options like "DecoratorsChangeTypes" ?

@ssalbdivad
Copy link

This feature would be a big QOL improvement for TS developers wanting to rely on composition over inheritance in their classes.

It would be very natural to allow the wrapper method to have its own signature which, to me, would substantially increase the utility of existing decorator functionality.

@finom
Copy link

finom commented May 22, 2024

I hope it's going to be implemented right after decorators reach stage 4. Currently it doesn't make a lot of sense to be able to change signatures while decorators aren't standardised, unfortunately.

@sybereal
Copy link

sybereal commented Nov 7, 2024

To add onto the validation topic, I believe that is actually a very convenient case for changing the signature. When working with a library like Zod, it is not rare for the result of the parsing operation to have a different type than the input, even if it's just removing null or undefined from property types.

In my case, I wanted to write a decorator to do exactly that, i.e., running a method's input through a provided Zod schema first before calling the method itself, and then passing the result to the method. Since this is a use case that happens in many different places in the code, I thought that a decorator would be a great abstraction to avoid having to replicate the manual parse invocation at all usage sites.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

7 participants