-
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
Ability to decorate abstract member function #37067
Comments
+1, would really love to have it. My use case is generating IPC proxies for classes in an electron app. |
I'd love to see this implemented. And not just for abstract classes but for interfaces too. It's handy as a hint for TypeScript compiler - custom transformers. No need to emit anything into final JavaScript code in case of interfaces. I work on TypeScript runtime reflection which generates a lof of usefull data to metadata library you can read at runtime. You can lookup functions, interfaces, classes their constructors, methods, parameters, decorators etc. |
The ability to decorate a member function of an abstract class is not the same as for an interface : an interface is not in the value space and is erased after the compilation. Conversely, an abstract class is just turned to a class in JS, it is even possible to instanciate it. So far, typescript doesn't allow that, so I used such ugly code instead : function Foo(target: any) {} // class decorator
function Bar(target: any, propertyKey?: string) {} // method decorator
@Foo
abstract class Test {
@Bar
existingMethod(): Date
{ throw '' } // dummy code (here is the ugly code)
}
const t : Test = new (Test as any)();
// just to show that instanciation of an abstract class is not a problem at runtime Then, I show you how JS code is generated (copy/paste from typescript playground) : var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
function Foo(target) { }
function Bar(target, propertyKey) { }
let Test = class Test {
existingMethod() { throw ''; }
};
__decorate([
Bar,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Date)
], Test.prototype, "existingMethod", null);
// below, some code that would have been generated on an abstract method, if that were allowed
__decorate([
Bar,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Date)
], Test.prototype, "nonExistingMethod", null);
// end of addon
Test = __decorate([
Foo
], Test);
const t = new Test(); I have inserted the code that would be generated if an abstract method I also run this code, with and without the presence of With that abstract class and in modern javascript (typically interception with Proxy), it makes sense to have an abstract member decorated. Please just remove in the compiler that error : |
@RyanCavanaugh Hi, as shown in my previous comment, it seems that decorating an abstract member function of an abstract class should be allowed ? What kind of feedback is still missing ? |
@ppoulard we don't really want to change decorator semantics right now (especially in a way that allows previously-unallowed things) since there are proposals working through the TC39 committee to standardize them |
Really would like to see the error removed. Using decorators right now to create a schema using abstract classes with dummy code. |
Would this feature be available in the ECMA decorator? @RyanCavanaugh |
Looks like now it's impossible to use it on abstract class members, then current implementation would look like this: @ClassDecorator
abstract class DummyAbstractClass {
protected fooValue!: number;
protected abstract get _foo(): number; // property
protected abstract set _foo(value: number); // property
protected abstract _bar(): void; // method
@memberDecorator
public get foo(): number {
return this._foo;
}
// TS error: Decorators cannot be applied to multiple get/set accessors of the same name.
// @memberDecorator is redundant here it applied to both of setter/getter
public set foo(value: number) {
this._foo = value;
}
@memberDecorator
public bar(): void {
return this._bar();
}
}
class DummyClass extends DummyAbstractClass {
protected get _foo(): number {
return this.fooValue;
}
protected set _foo(value: number) {
this.fooValue = value;
}
protected _bar(): void {
console.log('_bar called');
}
} Code is available on TS Playground ** feature waiting room >w< ** |
This will be very awesome to have it! But so far the only way (not best one, as it ignores errors to the whole code block) is having
Those way would be near perfect temporary solution if would be possible to specify certain error code to skip at |
+1 |
Here is the "not so dirty" solution I use to call decorators on methods that should be abstract: export const abstract = <T extends any>(): T => {
throw new Error(
'abstract() placeholder should be superseded by a decorator',
}
};
interface User {
// ...
}
abstract class SomeClass {
@Decorator()
method() {
return abstract<User>()
}
} This is what I implemented in my library AspectJS, which I want to use exactly to design a retrofit-like library for JS. |
Another way of doing this is by decorating class fields and typing them as functions: class Foo {
@implementSomething
getUsers!: () => Promise<User[]>
@implementSomething
saveUsers!: (user: User) => void
}
const foo = new Foo()
// This gets typed correctly now
foo.getUsers()
foo.saveUser(null) @RyanCavanaugh since Decorators are now in Stage 3 and changes to the proposal are not expected (unless absolutely necessary), could we revisit this idea? My idea is: we should implement decorated abstract methods in the same way as class fields, somewhat of a syntax sugar. Meaning when calling the decorator, Things to consider with my idea:
And since we discussing this, because SWC doesn't do any type-checking whatsoever, it does output valid JavaScript for abstract methods, and it does work in some way, although not fully compliant with the latest spec. Edit: My first version had some grammar errors, and a special function for the field declaration, I remebered the null-assertion field syntax can be used instead |
Search Terms
I search "decorate abstract member" in the issues list, and #20887 has memtioned this issue but closed.
Suggestion
Ability to decorate abstract function and its params.
Use Cases
To implement a retrofit-like http request library with Typescript. Examples from retrofit website:
When translate it into Typescript, we need to decorate an abstract class since interface will be erased after compilation. But,
To workaround such limitation, we cannot use abstract class, so it turns out to be
This is obviousely not elegant.
I think such ability can be implemented without breaks existing valid code.
Decorator function can determin wheter it's decorating an abstract member by check if value of property descriptor is undefined.
Since when targetting ES3,
PropertyDescriptor.value
is always undefined, this feature shall only be supported when targetting ES5-onward.Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: