如何组合多个 TypeScript 类装饰器?

Com*_*Cow 7 decorator typescript

我有一个类装饰器家族,我在许多类中重复使用它们。类似的东西:

@foo
@bar
@baz
export class MyClass { /* ..... */ }
Run Code Online (Sandbox Code Playgroud)

由于我在多个类中使用这三个装饰器,我真的很想将其分解为一个装饰器,如下所示:

@standard
export class MyClass { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

我试图创建一个新的类装饰器,它像这样链接装饰器调用:

export function standard<ReturnType>(ctor: Constructor<ReturnType>) {
    return baz(bar(foo(ctor)));
}
Run Code Online (Sandbox Code Playgroud)

TypeScript 手册说应用多个装饰器应该评估类似于函数组合,这就是为什么我认为我应该能够将它们链接在一起。但是,到了编译时间(使用 TypeScript 1.8)我得到一个类似于

Unable to resolve signature of class decorator when called as an expression. Type 'Constructor<ReturnType>' is not assignable to type 'void'.

有没有办法可以构造这个“包装器”装饰器来简化我的代码?

Com*_*Cow 5

在尝试为@David 构建我的问题的更完整版本时,我找出了哪里出了问题。

一个更完整的例子:

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; }
Run Code Online (Sandbox Code Playgroud)

我的实际代码中存在一些隐式键入,这在某种程度上向我隐藏了问题。显然我无法正确读取编译器输出。

问题在于我如何声明我的装饰器:

function foo(Target: Constructor<A>): Constructor<A> { }
Run Code Online (Sandbox Code Playgroud)

需要是

function foo<T extends A>(Target: Constructor<T>): Constructor<T> {}
Run Code Online (Sandbox Code Playgroud)

我注意到如果我将装饰器中的返回类型设置为any编译错误就会消失。额外的泛型参数让类型信息干净地流经装饰器。否则我相信它(基本上)看到Constructor<MyClass>无法分配实例Constructor<A>(因为A缺少其他接口)。d另外奇怪的是,如果我添加in的声明,我会在装饰器中弹出更多错误MyClass

所以最后 - 当使用类装饰器时,要小心你的泛型,或者只是 return any