Repository navigation
Description
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 ctsc 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
Activity
gund commentedon 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:
soffyo commentedon 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 commentedon Jul 25, 2023
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 commentedon 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 commentedon 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
nullorundefinedfrom 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.
Jamesernator commentedon Jan 19, 2025
It would be particularly helpful for decorators for decorators to be able to change the class type as well, for example for adding extra methods:
Arlen22 commentedon Apr 18, 2025
Here's another example where it would be very useful. I'm surprised this isn't supported already.
ayden94 commentedon Jul 31, 2026
Completed implementation evidence
I completed a TypeScript-Go/Corsa implementation of type-changing standard method decorators. This is an experimental language extension and implementation artifact, not a claim that the semantics have been accepted for upstream TypeScript.
prototype/decorator-method-final-typeb52b692cProposed semantic model
The source method signature and the decorated member type have separate roles:
ClassMethodDecoratorContext.For example:
The body of
methodstill uses(value: number) => number, while the observable type ofExample.prototype.methodis(value: string) => number.Stage-result rules
void,undefined, andneverpreserve the incoming callable.anyremainsany.unknownresults retain their diagnostics and recover to the last valid callable, allowing later decorators to continue composing.Under
strictNullChecks: false, explicitundefinedcan disappear during type normalization. The implementation therefore tracks fallback provenance through aliases, conditional types, inferred returns, explicitly typed identifier expressions, and uncertain indexed-access forms. Recursive analysis is bounded; indeterminate cases preserve the incoming callable rather than unsafely narrowing it away.Supported scope
The implementation covers:
void/undefinedresultsthis, static versus instanceThis, andcontext.static#privatemethods without widening accessDeclaration emit
Declaration emit uses method syntax when one ordinary call signature completely represents the final type. It uses property syntax when method syntax would lose information such as unions, overload sets,
any, construct signatures, properties, or index signatures.For checked JavaScript, a member whose public type changes does not reuse stale type-bearing JSDoc. Unchanged JavaScript members and TypeScript API documentation retain their existing documentation behavior.
Intentional exclusions
This implementation does not apply decorator-driven member-type mutation to:
isolatedDeclarationsThese are explicit boundaries of this implementation, not assertions that a future language design cannot support them.
Verification
The branch includes focused checker, compiler, declaration, runtime, incremental, and Fourslash coverage for the supported behavior and exclusions. In particular, it tests overload composition, callable facets, generic/alias instantiation, both conditional-union orders, non-strict-null fallback provenance, query-order independence, incremental edits, and language-service edits.
The current implementation passes:
npx hereby testDecisions needed for an upstream design