我正在尝试在打字稿中创建一个类装饰器。 我想调用该类的一些方法。此外,我想约束 装饰器只能应用于从特定抽象类派生的类。
这是我尝试做的事情:
abstract class BaseValidation {
public abstract getValidationName():string;
public abstract validation():boolean
}
function ValidationDecorator() {
return function <T extends BaseValidation>(target: T) {
let validationName = target.getValidationName();
}
}
@ValidationDecorator()
class SomeValidation extends BaseValidation{
public getValidationName():string{
return "ValidationName";
}
public validation():boolean{
return false;
}
}
这是我得到的错误:
类型'SomeValidation'的参数不能分配给'BaseValidation'类型的参数。 类型'typeof SomeValidation'缺少类型'BaseValidation'的以下属性:getValidationName,validation
A link to typescript playground that showing the error
根据错误,我了解到'typeof SomeValidation'通过了 到装饰者。
有什么想法可以实现上面描述的内容吗?
谢谢!
答案 0 :(得分:0)
问题是class decorator应用于类的构造函数,而不是类的 instance 。
也就是说,给定类似Ctor<T>
的构造函数类型
type Ctor<T> = {
new(...args: any[]): T;
prototype: T
}
这两者都是new
并具有prototype
属性,您的修饰器需要使用Ctor<BaseValidation>
,而不是BaseValidation
:
function ValidationDecorator() {
return function<T extends Ctor<BaseValidation>>(target: T) {
// impl
}
}
关于您在实现中做的事情……那么,您将只能访问构造函数,而不能访问任何实例。您可以在原型上调用方法,前提是它们不需要完全配置的实例即可工作。例如,假设您正在维护从返回值validationName()
到类构造函数的映射(您在注释中提到了“ validationFunction”,但我在任何地方都看不到;大概可以根据需要进行调整)至)。该映射可以是普通的旧对象,而不是Map
,因为字符串键是普通的旧对象最擅长的:
const validationMap: { [k: string]: Ctor<BaseValidation> | undefined } = {};
然后您可以将ValidationDecorator()
实施为:
function ValidationDecorator() {
return function<T extends Ctor<BaseValidation>>(target: T) {
// assuming that getValidationName is a method defined on the prototype
validationMap[target.prototype.getValidationName()] = target;
};
}
同样,请注意,target.prototype.getValidationName()
仅在getValidationName()
方法不需要其类的实例才能工作时才起作用。如果它只是返回一个常量字符串,甚至不看this
,那么就可以了:
@ValidationDecorator()
class SomeValidation extends BaseValidation {
public getValidationName(): string {
return "ValidationName";
}
public validation(): boolean {
return false;
}
}
@ValidationDecorator()
class SomeOtherValidation extends BaseValidation {
public getValidationName(): string {
return "OtherValidationName";
}
public validation(): boolean {
return false;
}
}
我们可以验证它是否有效:
Object.keys(validationMap).forEach(k =>
console.log(k + ": " + (validationMap[k] || { name: "oops" }).name)
);
// ValidationName: SomeValidation
// OtherValidationName: SomeOtherValidation
所以看起来还可以。
当然,直接依赖原型的调用方法可能是个坏主意。也许您可以将getValidationName()
和validation()
方法更改为static
方法而不是实例方法?但是一旦我开始思考,我注意到您的示例代码并没有真正表明您为什么要为此使用类,而不仅仅是一些验证功能:
const simplerValidationMap = {
ValidationName: (thingToValidate: any) => false,
OtherValidationName: (thingToValidate: any) => false
}
({any
被您实际验证的任何类型替换)。也许BaseValidation
的子类具有它们可以管理的某些实例状态以及依赖于这种状态的方法?但是在那种情况下,我看不到从类的名称到非实例的映射对您有什么帮助。哦,好吧,我想这取决于您。
无论如何,希望能有所帮助。祝你好运!