我正在尝试在Typescript中实现类装饰器。所以我有一个以类作为参数的函数。
const decorate = function ()
{
return function ( target: any )
{
return class myExtendClass extends target{}
};
};
目标是使用它:
@decorate()
class Something{}
不幸的是我得到了type any is not a constructor function type
。知道如何实现我的目标吗?
答案 0 :(得分:4)
我的问题是为什么这段代码不起作用,因为它的有效js
这取决于无法正常工作的含义。
生成的javascript有效且可以使用。但是如果你编写的代码不符合TS的规则,TypeScript会更加严格并且会抱怨,即使它通常会很好并且无论如何都会给你编译的js。
您尝试执行的操作的问题是TS不支持通过装饰器扩展类。它会生成有效的js,但是当你这样做时TS不了解发生了什么。
例如:
const decorate = () => (target: typeof Cat) =>
{
return class Lion extends target
{
public Roar = () => console.log("Roaar")
}
}
@decorate()
class Cat
{
public Purr = () => console.log("Purr purr");
}
var lion = new Cat();
lion.Roar(); // Error in TypeScript but working in js
当你运行它时,这是有效的,但TS不明白你在装饰器中做了什么,所以它不知道类Cat
实际上是Lion
。
有一个feature request让TS了解装饰器中的类突变,但此时它不受支持。
我建议你避免在像这样的装饰器中改变类。即使它可以工作,我也不会在你的例子中看到现在的观点,它不是很直观,如果你想让所有Something
具有某些属性,你可以扩展直接:Something extends MyReusableProperties
答案 1 :(得分:1)
你可以尝试:
interface Newable<T> {
new (...args: any[]): T;
}
function decorate()
{
return function (target: Newable<Object>)
{
return class myExtendClass extends target {
public someOtherProp: string;
publicconstructor() {
this.someOtherProp = "test2";
}
}
};
};
但是你会得到一个新的错误:
// Error: Type 'typeof myExtendClass' is
// not assignable to type 'typeof Something'.
@decorate()
class Something{
public someProp: string;
publicconstructor() {
this.someProp = "test";
}
}
您可以尝试修复它:
function decorate()
{
return function (target: Newable<Object>)
{
class myExtendClass extends target {
public someOtherProp: string;
publicconstructor() {
this.someOtherProp = "test2";
}
}
return (<any>myExtendClass); // notice casting!
};
};
再次你会得到另一个错误:
let something = new Something();
console.log(something.someProp);
// Property 'someOtherProp' does not
//exist on type 'Something'.
console.log(something.someOtherProp);
最后,您可以通过转换为任何:
来解决这个问题console.log((<any>something).someOtherProp); // More casting!
当然,这不是一个理想的解决方案。我会尝试做类似以下的事情:
interface Newable<T> {
new (...args: any[]): T;
}
function decorate()
{
return function (target: Newable<Object>)
{
class myExtendClass extends target {
public someOtherProp: string;
publicconstructor() {
this.someOtherProp = "test2";
}
}
return (<any>myExtendClass);
};
};
function applyDecorator<T,TDecorated>(decorator, ctr): Newable<TDecorated> {
// Decorators are just functions, you don't need @ symbol to use them :)
let decorated: Newable<TDecorated> = <any>decorator()(ctr);
}
interface ISomething {
someProp: string;
}
interface ISomethingElse extends Something {
someOtherProp: string;
}
class Something implements ISomething {
public someProp: string;
publicconstructor() {
this.someProp = "test";
}
}
let SomethingElse = applyDecorator<ISomething, ISomethingElse>(decorate, Something);
let something = new SomethingElse();
console.log(something.someProp);
console.log(something.someOtherProp);
希望有所帮助:)
答案 2 :(得分:0)
我认为问题在于装饰函数的签名。
将您的功能签名从 function ( target: any )
更改为 function <T extends { new (...args: any[]): {} }>
。
为了简单起见,语句class myExtendClass extends target
无错误地工作 target 必须是类又名构造函数,因为它在Javascript中调用。
您不能扩展到任何,而是扩展到类(构造函数)。
这里具有约束的泛型类型T将目标约束为类(构造函数)。因此extend
语句无错误地运行。
这是一个例子:
const decorate = function ()
{
return function <T extends { new (...args: any[]): {} }>
(target: T)
{
return class myExtendClass extends target{
anotherProperty = 10; //assigning class property here
propertyFromDecorator = "new property from decorator" // new property
}
};
};
@decorate()
class Something{
myProp: string;
anotherProperty:number;
constructor() {
this.myProp = "my property";
}
}
var someThing = new Something();
console.log(someThing);