On the TypeScript's Decorator reference page there is a code snipped that illustrates how to override the constructor with class decorator:
function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
return class extends constructor {
newProperty = "new property";
hello = "override";
}
}
@classDecorator
class Greeter {
property = "property";
hello: string;
constructor(m: string) {
this.hello = m;
}
}
console.log(new Greeter("world"));
and in logs:
class_1 {
property: 'property',
hello: 'override',
newProperty: 'new property' }
So far so good. BUT trying to access newProperty
by dot notation fails with:
Property 'newProperty' does not exist on type 'Greeter'.ts(2339)
error and it's not listed in hints in VS Code. One can access it by bracket notation but TS warns that
Element implicitly has an 'any' type because type 'Greeter' has no index signature.ts(7017)
Am I missing something? How to implement adding new properties via Decorators in type-safe way? I'd like to have normal compiler support just like with regular class members.
Decorators by design can't change the type of a class. This is stil in discussion and it appears until the decorator proposal is finalized the team will not change the behavior. You can use mixins for this task (read about mixins in ts)
Using mixins the code would look something like:
function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
newProperty = "new property";
hello = "override";
}
}
const Greeter = classDecorator(class {
property = "property";
hello: string;
constructor(m: string) {
this.hello = m;
}
});
type Greeter = InstanceType<typeof Greeter> // have the instance type just as if we were to declare a class
console.log(new Greeter("world").newProperty);
function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
newProperty = "new property";
hello = "override";
}
}
interface classInterface {
newProperty: string;
hello: string;
}
//trick
interface Greeter extends classInterface { };
@classDecorator
class Greeter {
property = "property";
hello: string;
constructor(m: string) {
this.hello = m;
}
}
const b = new Greeter();
console.log(b.newProperty);
Seems we can use interface trick to solve the problem. Reference of the trick: https://stackoverflow.com/a/52373394/4831179
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With