typescript - Typ 'any' är inte en konstruktörsfunktionstyp
Jag försöker implementera en klassdekorerare i Typescript. Så jag har en funktion som tar en klass som ett argument.
const decorate = function ()
{
return function ( target: any )
{
return class myExtendClass extends target{}
};
};
Målet är att använda det så här:
@decorate()
class Something{}
Tyvärr får jag type any is not a constructor function type. Någon idé om hur jag kan uppnå det jag vill?
Svar #1
Min fråga är varför den här koden inte fungerar, eftersom den är giltig JS
Det beror på vad du menar med fungerar inte.
Den genererade JavaScript-koden är giltig och kommer att fungera. Men TypeScript är mycket striktare och kommer att klaga om du skriver kod som inte följer TS-reglerna, även om den ofta ändå fungerar bra och ger dig den kompilerade JS-koden.
Problemet med det du försöker göra är att TS inte har stöd för att utöka klasser via dekoratorer. Det kommer att producera js som fungerar, men TS förstår inte vad som händer när du gör detta.
Till exempel:
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
Detta fungerar när du kör det, men TS förstår inte vad du gör inom dekoratorn, så det vet inte att klassen Cat faktiskt är en Lion.
Det finns en funktionförfrågan om att få TS att förstå klassmutationer i dekoratörer, men för tillfället stöds det inte.
Jag föreslår att du undviker att förändra klasser i dekoratorer på det här sättet. Och även om det skulle fungera, ser jag inte poängen med ditt exempel som det är nu, det är inte särskilt intuitivt och om du vill att alla Something ska ha vissa egenskaper kan du bara utöka det direkt: Something extends MyReusableProperties
Svar #2
Du kan försöka:
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";
}
}
};
};
Men då kommer du att få ett nytt fel:
// Error: Type 'typeof myExtendClass' is
// not assignable to type 'typeof Something'.
@decorate()
class Something{
public someProp: string;
publicconstructor() {
this.someProp = "test";
}
}
Du kan försöka fixa det:
function decorate()
{
return function (target: Newable<Object>)
{
class myExtendClass extends target {
public someOtherProp: string;
publicconstructor() {
this.someOtherProp = "test2";
}
}
return (<any>myExtendClass); // notice casting!
};
};
Och igen kommer du att få ett annat fel:
let something = new Something();
console.log(something.someProp);
// Property 'someOtherProp' does not
//exist on type 'Something'.
console.log(something.someOtherProp);
Slutligen kunde du lösa det genom att casta till any:
console.log((<any>something).someOtherProp); // More casting!
Självklart är detta inte en idealisk lösning. Jag skulle försöka göra något liknande följande istället:
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);
Hoppas det hjälper :)
