Reputation: 38140
Let's say I have a decorator like in the following
example
which adds a new name
property with a preset value.
Is there any way to tell typescript that my decorator adds this property so that all decorated classes are typed correctly?
Example code:
function withName(name) {
return (target) => {
target.prototype.name = name;
}
}
@withName('Karl')
class Person {
greet() {
console.log("Hi I'm ", this.name);
}
}
const karl = new Person();
karl.greet(); // Will log: "Hi I'm Karl"
console.log(karl.name); // <- Will cause a TypeScript error: "Property 'name' does not exist on type 'Person'"
Upvotes: 3
Views: 174
Reputation: 249646
Decorators by design are not allowed to change the structure of the type they are decorating. A supported way of doing this would be to use a mixin, as described here
function withName(nameValue: string) {
return <T extends new (...args: any[]) => any>(target: T) => {
return class extends target {
name: string = nameValue
}
}
}
const Person = withName("Karl")(class {
greet() {
console.log("Hi I'm ...");
}
});
const karl = new Person();
console.log(karl.name);
The approach above only augments the class to the outside world so the added members are not visible from inside the class. We could augment the base class of Person
(or use an empty class if there is no base class) to get access to the added fields inside the augmented class:
class Person extends withName("Karl")(class { }) {
greet() {
console.log("Hi I'm ", this.name);
}
}
const karl = new Person();
karl.greet(); // Will log: "Hi I'm Karl"
Upvotes: 3