-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Feature request: Analyze class decorators for modifications of the decorated class's prototype #8250
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
Comments
Oh, I wasn't aware of these issues. Thanks! Closing as dup. |
I read through all these issues and imo this is a seperate issue. Per the documentation, a class decorator can return a new class that replaces the definition: function myDecorator<T>(constructor: T): T & {staticMethod(): string} {
constructor.staticMethod = function(): string {
return 'hello';
}
return constructor;
} But the return type is not used: @myDecorator
class Hello { }
Hello.staticMethod() // method staticMethod() does not exist on type Hello |
That is an interesting point. Currently, there is no type analysis/mutation done on decorators, even ones that can return a value. I had always assumed design-time/ambient decorators would be how this would be solved, but you are right, the type analysis could be done like that. @rbuckton / @mhegazy was there a reason why the return types from decorators aren't used in the type analysis? |
This would be super useful for sequelize models: const attributeOptions = Symbol('attributeOptions')
function Attribute(attribute: string | Seq.ABSTRACT | typeof Seq.ABSTRACT | Seq.DefineAttributeColumnOptions) {
return Reflect.metadata(attributeOptions, attribute);
}
function Model<T>(sequelize: Sequelize.Connection, options: Sequelize.DefineOptions<T>) {
return function Model<U extends Function>(Model: U): Sequelize.Model & U {
const attributes: Seq.DefineAttributes = {};
if (!options.instanceMethods) {
options.instanceMethods = {}
}
if (!options.classMethods) {
options.classMethods = {}
}
if (!options.getterMethods) {
options.getterMethods = {}
}
if (!options.setterMethods) {
options.setterMethods = {}
}
for (const key in Model.prototype) {
if (typeof Model.prototype[key] === 'function') {
// method
options.instanceMethods[key] = Model.prototype[key]
} else {
const descriptor = Object.getOwnPropertyDescriptor(Model.prototype, key)
if (descriptor.get) {
options.getterMethods[key] = descriptor.get
}
if (descriptor.set) {
options.setterMethods[key] = descriptor.set
}
if (Reflect.hasMetadata(attributeOptions, Model)) {
// property
attributes[key] = Reflect.getMetadata(attributeOptions, Model.prototype, key)
}
}
}
// static
for (const key in Model) {
if (typeof Model.prototype[key] === 'function') {
// method
options.classMethods[key] = Model.prototype[key]
}
}
const SequelizeModel = sequelize.define(Model.name, attributes, options);
return <Sequelize.Model<U, U> & U>SequelizeModel;
}
} Usage: const sequelize = new Sequelize(process.env.DB_CONNECTION)
@Model(sequelize, {tableName: 'users'})
class User {
@Attribute({type: Sequelize.STRING, primaryKey: true})
public username: string;
static myCustomStaticMethod() {
return 'hello'
}
}
User.findOne() // should work
User instanceof Sequelize.Model // should be true
User.myCustomStaticMethod() // should work |
This is an implementation limitation. the system as it is built today does not allow for mutating the shape of a declaration after it has been analyzed and bound. different parts of the compiler depends on this assumption. changing this would require some significant rejiggering. |
How is it possible to type a decorator that adds a property to the class? |
I meant declarative forms instead of the decorator (which I have consider imperative). so the declarative form of adding a property to a class would be a property declaration, the imperative form is a decorator that defines adds a property. |
When decorating a class and adding properties to the class's prototype the compiler currently does not present the added properties in the auto-completion.
It would be nice if this feature would be implemented because right now to use such a decorator in a type-safe way one has to define an interface that the decorated class has to implement.
The text was updated successfully, but these errors were encountered: