Reputation: 356
I want to define a decorator signature for class decorator in typescript for a javascript code that already exists. Let's say I have some already defined class Foo
class Foo {
someMethod() {
}
}
Then there is also some class decorator method which take input class but it requires that the input class has some other method defined before it is decorated.
function decorate(someClass) {
// some properties and methods injection happens here
}
Then in my typescript code I would create a class to decorate as:
class Bar extends Foo {
requiredMethod(): any {
return null;
}
}
Then for definitions I tried:
interface WithRequiredMethod {
requiredMethod(): any;
}
declare function decorate<C extends Foo>(input: C & WithRequiredMethod): C;
And then call it as:
const DocoratedBar = decorate(Bar);
Unfortunately typescript doesn't recognise Bar as valid input even if it has requiredMethod
defined. Is there any way to define decorate
signature to validate presence of requiredMethod
on input class?
Upvotes: 0
Views: 590
Reputation: 31823
You almost have it. But you need to write this
interface WithRequiredMethod {
// tslint:disable-next-line:no-any
requiredMethod(): any;
}
declare function decorate<C extends new () => Foo & WithRequiredMethod>(target: C): C;
The reason is that the decorator targets a class. A class is really just a constructor function. So the object that gets decorated is actually a constructor function for objects of type Foo & WithRequiredMethod
.
For example, if we only wanted to restrict our decorator to targeting Foo
or one of its subclasses we would write
declare function decorate<T extends typeof Foo>(target: T): T;
Interestingly, the error originally received is stating that class Bar
does not implement WithRequiredMethod
, which is indeed true, but not what we wanted to say. But, it could well be useful to say and when so we can leverage decorators to enforce both static and instance properties.
Consider:
declare function decorate<
C extends WithRequiredMethod & (new () => Foo & WithRequiredMethod)
>(target: C): C;
Now we apply this to class Bar
and again receive the error that requiredMethod
is missing. We can however satisfy the requirement by adding a staticrequiredMethod
method to Bar
:
@decorate class Bar extends Foo {
// required by `decorate`
static requiredMethod() {
// tslint:disable-next-line:no-null-keyword
return null;
}
// also required by `decorate`
requiredMethod() {
// tslint:disable-next-line:no-null-keyword
return null;
}
}
Upvotes: 1