TypeScript:无法解析通用 class 装饰器的签名

TypeScript: failure to resolve signature of generic class decorator

我无法理解为什么 TypeScript 在这种情况下需要 Child2Child3 的显式泛型类型定义:

abstract class Base {
    public static A: string = "Fallback_A";
    public DoSmthWithClassName(): string {
        return "Fallback_DoSmth";
    }

    constructor(...args: any[]); // overload for type consistency with children
    constructor(x: string)
    { }
}

// typeof any non-abstract child of Base
type BaseType = typeof Base & (new(...args: any[]) => Base);

// decorator, modifies methods and static properties
function ClassDecorator<T extends BaseType>(valueA: string): (value: T) => T {
    return (value: T) => {
        value.prototype.DoSmthWithClassName = () => value.name + ".DoSmth." + value.A;
        value.A = value.name + valueA;
        return value;
    }
}

@ClassDecorator("Foo") // OK
class Child0 extends Base {

}

@ClassDecorator("Foo") // OK
class Child1 extends Base {
    constructor(x: number) {
        super(x.toString());
    }
}

@ClassDecorator("Foo") // Unable to resolve...
class Child2 extends Base {
    static X: number = 0;
}

@ClassDecorator<typeof Child3>("Foo") // OK
class Child3 extends Base {
    static X: number = 0;
}

问题是 TS 无法从唯一的参数 valueA 推断出 T 的类型。您想要的是 inner/returned 函数的通用参数:

// decorator, modifies methods and static properties
function ClassDecorator(valueA: string) {
    return function <T extends BaseType>(value: T): T {
        value.prototype.DoSmthWithClassName = () => value.name + ".DoSmth." + value.A;
        value.A = value.name + valueA;
        return value;
    }
}

您的版本不会导致 Child0 和 Child1 出现任何问题,因为它们在结构上与 base 相同。

来不及回答了,不知道是否还符合需求。但是,不需要 ClassDecorator 中的通用参数,您仍然可以在其中使用 'static X' 实例化 Child2 并通过 Child2.X.

访问 X

这是因为没有构造函数的 ClassDecorator 就像没有船的经验丰富的水手,它需要一个类型,以便 Typescript 可以确定将其编译为函数。

最好的解决方案是在 ClassDecorator 之后反映它,

const Reflected = (value:string) : ClassDecorator => {
  return reflected => { 
    // `reflected` here is `Type<Foo>`, not `Foo`
    console.log(`The reflected:  ${reflected}`)
  };
};

您可以立即使用它,因为 @Reflected("Foo") 不是新实例,

//@ClassDecorator("Foo") // Unable to resolve...
@Reflected("Foo")
class Child2 extends Base {
  public static X: number = 0;
  
}

我能想到的一些原因:

  • A class,基本上是 运行 时的 javascript 函数,太通用了,但常量实例不是(专注于类型),这就是为什么我们需要一个'dependency injection' 和其他好处的 ClassDecorator。 -在 Typescript 中,我们需要一种 'decorated' 实例,而不是设计时的实例本身,它在编译时提供正确的类型。

TS Playground

PS:如果您使用的是 'reflect-metadata' 包,您可以使用 console.log(Reflect.getMetadata('design:paramtypes', reflected)); 进一步了解 我还没有看到任何副作用,如果发现任何副作用,我会更新。