-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Type loss when using generic decorators #17795
Comments
You can work around it by returning a generic decorator from your decorator factory. function decorate() {
return <T extends Base, K extends keyof T, F extends T[K]>
(proto: ProtoOf<T>, propertyKey: K, descriptor: TypedPropertyDescriptor<F>) => {
// Do stuff.
};
}
class Test extends Base {
@decorate() bar(): boolean {
return false;
}
} I think this behavior is correct since it is equivalent to writing function decorate<T extends Base, K extends keyof ProtoOf<T>, F extends T[K]>() {
return (proto: ProtoOf<T>, propertyKey: K, descriptor: TypedPropertyDescriptor<F>) => {
// Do stuff.
};
}
const decorator = decorate();
class Test extends Base {
@decorator bar(): boolean {
return false;
}
} |
@aluanhaddad thanks for the update! Your solution does address the issue in my earlier example, though I guess the actual problem I was having had to do with decorator arguments:
So the decorator works on |
On another note, if |
Just nothing that this So @aluanhaddad's suggestion seems like the correct solution here. |
Your explanation makes sense. Though the issue still stands that it doesn't seem currently possible to have decorate properties work off the decorated class. It would be great if there was a way to give "context" to decorator properties as to what exact object they're decorating. |
@vaskevich I'm not sure if I understand what you are trying to achieve correctly, but you can capture a string literal type parameter when the decorator factory is applied and then subsequently validate that this property exists on the class with the decorated method. So, going back to your example, you can validate that a (Warning these types are pretty hard to read and I experienced several language service crashes in VS code due to recursion while working them out.) type ProtoOf<T> = Pick<T, keyof T>;
function decorate<CK extends string>(property: CK) {
return <
T extends Base & {[P in CK]: G},
K extends keyof T,
F extends T[K] & G,
G extends ((...args: {}[]) => R),
R>(
proto: ProtoOf<T> & {[P in CK]: (...args: {}[]) => R},
propertyKey: K,
descriptor: TypedPropertyDescriptor<F>) => {
// Do stuff.
};
}
class Test extends Base {
@decorate('foo') bar(): boolean {
return false;
}
foo(): boolean {return false;}
} The way this works is by capturing a type for the argument to the factory and using that argument to define the expected shape of the object that will be decorated. The declaration of Note that the the declaration of T is provided, as in my previous example, by the decorator and not the decorator factory. |
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed. |
Thanks for the replies - I haven't been able to take a look at this again yet, but will open a new issue if there's a specific defect here (which doesn't seem to be the case). |
TypeScript Version: 2.4.1
The following code aims to restrict the decorator
decorate
to members of a class inheriting fromBase
. However, it seems thatK
ends up only including members inBase
, not in the inherited class. (This is a minimal reproducible example for other cases, e.g., restricting the decorator to methods within subclasses ofBase
of a certain return type.)Code
Expected behavior:
No errors when applying
@decorate()
tobar()
.Actual behavior:
Error:
[ts] Argument of type '"bar"' is not assignable to parameter of type '"base"'.
The text was updated successfully, but these errors were encountered: