Lägga till egenskaper till en klass via dekoratorer i TypeScript


På TypeScripts referenssida för dekoratorer finns ett kodavsnitt som illustrerar hur man åsidosätter konstruktorn med en klassdekorator:

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

@classDecorator
class Greeter {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}

console.log(new Greeter("world"));

och i loggar:

class_1 {
  property: 'property',
  hello: 'override',
  newProperty: 'new property' }

Hittills är allt bra. MEN att försöka komma åt newProperty med punktnotation misslyckas med:

Egenskapen 'newProperty' finns inte på typen 'Greeter'.ts(2339)

fel och det är inte listat i ledtrådar i VS Code. Man kan komma åt det med hakparentesnotation men TS varnar att

Element har implicit typen 'any' eftersom typen 'Greeter' inte har någon indexsignatur.ts(7017)

Missar jag något? Hur implementerar man att lägga till nya egenskaper via dekoratörer på ett typ-säkert sätt? Jag skulle vilja ha vanlig kompilatorstöd precis som med vanliga klassmedlemmar.

Svar #1

Dekoratorer kan enligt design inte ändra typen på en klass. Detta är fortfarande under diskussion och det verkar som att tills dekoratorförslaget är färdigställt kommer teamet inte att ändra beteendet. Du kan använda mixins för denna uppgift (läs om mixins i ts)

Med hjälp av mixins skulle koden se ut ungefär så här:

function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

const Greeter = classDecorator(class {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
});
type Greeter = InstanceType<typeof Greeter> // have the instance type just as if we were to declare a class

console.log(new Greeter("world").newProperty);

Svar #2

function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}
interface classInterface {
    newProperty: string;
    hello: string;
}

//trick
interface Greeter extends classInterface { };

@classDecorator
class Greeter {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}
const b = new Greeter();
console.log(b.newProperty);

Verkar som att vi kan använda gränssnittstricket för att lösa problemet. Referens för tricket: https://stackoverflow.com/a/52373394/4831179

Relaterade artiklar