I have a family of class decorators that I repeat over many classes. Something similar to this:
@foo
@bar
@baz
export class MyClass { /* ..... */ }
Since I'm using those three decorators across multiple classes, I'd really like to break that down into one decorator, like so:
@standard
export class MyClass { /* ... */ }
I've tried to create a new class decorator that chains the decorator calls like this:
export function standard<ReturnType>(ctor: Constructor<ReturnType>) {
return baz(bar(foo(ctor)));
}
The TypeScript handbook says applying multiple decorators should evaluate similar to function composition, which is why I figured I should be able to chain them together. However, come compile time (using TypeScript 1.8) I get an error similar to
Unable to resolve signature of class decorator when called as an expression. Type 'Constructor<ReturnType>' is not assignable to type 'void'.
Is there a way I can construct this 'wrapper' decorator to simplify my code?
In an attempt to build a more complete version of my problem for @David, I figured out where I was going wrong.
A more, full example:
interface Constructor<T> { new(...args: any[]): T }
interface A { a: number; }
interface B { b: number; }
interface C { c: number; }
function foo(Target: Constructor<A>): Constructor<A>{
// do some stuff to the constructor
return Target;
}
function bar(Target: Constructor<B>): Constructor<B> {
// do some stuff to the constructor
return Target;
}
function baz(Target: Constructor<C>): Constructor<C> {
// ....
return Target;
}
function standard(ctor: Constructor<A & B & C>): Constructor<A & B & C> {
return baz(bar(foo(ctor)));
}
@foo
@bar
@baz
class MyClass implements A, B, C { a=1;b=2;c=3;d=6; }
There was some implicit typing in my actual code that somewhat hid the problem from me. And apparently I couldn't read compiler output correctly.
The problem was with how I declared my decorators:
function foo(Target: Constructor<A>): Constructor<A> { }
Needed to be
function foo<T extends A>(Target: Constructor<T>): Constructor<T> {}
I noticed if I set the return types in the decorators to any
the compile errors went away. The extra generic parameter let the type info cleanly flow through the decorators. Otherwise I believe it saw (essentially) that Constructor<MyClass>
could not be assigned an instance of Constructor<A>
(since A
is missing the other interfaces). Also curiously, I got more errors in the decorators to pop up if I added that declaration of d
in MyClass
.
So in closing - when working with class decorators, be careful with your generics, or just return any
.