2

我不明白为什么 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;
}
4

2 回答 2

2

问题是 TS 无法从唯一的参数推断 T 的类型valueA。你想要的是内部/返回函数的泛型参数:

// 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 相同。

于 2017-08-01T00:31:08.623 回答
0

现在回答太晚了,我不知道它是否仍然适合需要。但是,不需要 ClassDecorator 中的泛型参数,您仍然可以在其中实例化 Child2 并在其中使用“静态 X”并通过Child2.X.

这是因为没有构造函数的 ClassDecorator 就像没有船的有经验的水手一样,它需要一个 Type 以便 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;
  
}

我能想到的一些原因:

  • 一个类,基本上是运行时的 javascript 函数,过于通用,但常量实例不是(专注于类型),这就是为什么我们需要一个 ClassDecorator 来实现“依赖注入”和其他好处。-在 Typescript 我们需要一种“装饰”实例,而不是在设计时实例本身,它在编译时提供正确的类型。

TS游乐场

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

于 2020-10-23T23:57:38.533 回答