Typescript-dekoratorfunktion generisk typning


Jag skapar ett Express REST API och tittar också på att skapa mina egna dekoratorfunktioner. Jag skapar nu en dekorator för en controller-klass.

Men varje gång jag vill lägga till denna dekoratör på kontrollern får jag följande fel:

Unable to resolve signature of class decorator when called as an expression. Type 'Constructable' is not assignable to type 'typeof ZoneController'. Property 'getZones' is missing in type 'RestController' but required in type 'ZoneController'.

Det här felet dyker inte upp i Stackblitz (det borde dyka upp vid dekoratorn på ZoneController), men för koden se Stackblitz

Detta är logiskt eftersom RestController är föräldraklassen till ZoneController, så den känner inte till några metoder. För att lösa detta vill jag lägga till en generisk typning till controller-dekoratorn:

type Constructable<T> = new (...args: any[]) => T

export function Controller<T extends RestController>(restCtor: Constructable<T>): Constructable<T> {
  return class extends restCtor {
    router = null
  }
}

Men detta orsakar följande fel:

Class '(Anonymous class)' incorrectly extends base class 'T'. '(Anonymous class)' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'RestController'

Jag vet inte hur jag ska lösa detta fel. För felet och koden, se följande Stackblitz. Jag har hittat viss dokumentation (här och här) om detta fel, men det är oklart för mig hur jag ska ändra min kod för att lösa detta fel.

Hur bör jag ändra min kod för att lösa detta problem? Jag använder TypeScript v4.0.2 och följande TSConfig:

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "module": "esnext",
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  }
}

Svar #1

Definitionen för en klass kan skrivas med hjälp av ett interface:

interface SomeClass {
  new(...args: any[]): SomeClass
}

Följande samma mönster tror jag att du kommer vilja ändra din definition av Constructable:

type Constructable<T> = new (...args: any[]) => Constructable<T>