Reputation: 1128
How to define property with TypeScript and decorators?
For example I have this class decorator:
function Entity<TFunction extends Function>(target: TFunction): TFunction {
Object.defineProperty(target.prototype, 'test', {
value: function() {
console.log('test call');
return 'test result';
}
});
return target;
}
And use it:
@Entity
class Project {
//
}
let project = new Project();
console.log(project.test());
I have this console log:
test call entity.ts:5
test result entity.ts:18
This code correctly worked, but tsc return error:
entity.ts(18,21): error TS2339: Property 'test' does not exist on type 'Project'.
How to fix this error?
Upvotes: 13
Views: 11540
Reputation: 35
I was able to make it work with the combination of decorator and mixin.
Firstly, you need to defined your mixin.
NOTE : The use of mixin is not mandatory, but the mixin pattern allow multiple inheritance and more flexibility
export function YourMixin<T extends YourConstructor>({ Base }: MixinProps<T>) {
class Mixin extends Base {
// You can define other decorators here
async method() {
// your logic
}
}
return Mixin;
}
// Define your constructor type.
export type Constructor<T, Arguments extends unknown[] = any[]> = new (...arguments_: Arguments) => T;
export type YourConstructor<T = { }> = Constructor<T>;
// Define your interface with the same name of your decorator to enable declaration merging.
export interface YourDecorator {
async method(): Promise<void>;
}
// Define your decorator
export function YourDecorator(options = {}) {
return function <T extends YourConstructor>(Base: T) {
// You can define other decorators here
class YourClass extends YourMixin(Base) { }
// Apply original class descriptors to the new class
const ownPropertyDescriptors = Object.getOwnPropertyDescriptors(Base);
const { prototype, ...descriptors } = ownPropertyDescriptors;
Object.defineProperties(YourClass, descriptors);
return YourClass as T;
}
}
// Use your decorator and define your class interface that extends the decorator interface
// for declaration merging types.
interface Service extends YourDecorator {}
@YourDecorator({})
class Service {
}
Upvotes: 0
Reputation: 22322
Af far as I know for now this is not possible. There is a discussion on this issue here: issue.
So you either do not use decorators to extend classes, and maybe if appropriate use interfaces with declaration merging to extend type declaration. Or use type assertion (but loosing type checks): (<any>project).test()
Upvotes: 9