generics - Version av abstrakta metoder för statiska metoder i TypeScript


Jag skapar ett datamodellsparadigm med TypeScript. Jag lagrar olika typer av data på olika ställen (SQL, en lokal cache). Jag vill skapa en abstrakt klass som innehåller alla metoder jag skulle behöva för vilken typ av datalagring som helst (create, find, update, count, destroy). På så sätt skulle jag kunna utöka den klassen och implementera den olika för olika typer av lagring, och kompilatorn skulle varna mig om jag saknade en metod. Jag skulle sedan utöka en av dessa implementationer i en klass som beskriver datamodellen. Men vissa av de metoder jag behöver (till exempel find och create) är statiska. Jag vet att TypeScript inte stödjer abstrakta statiska metoder. Finns det något liknande abstrakta metoder som jag kan använda så att kompilatorn varnar mig om saknade metoder?

Jag skulle också vilja att dessa statiska metoder är generiska och typade på samma sätt som klassen. Jag vet att detta inte gör någon mening för en standardgenerisk klass. Men eftersom denna klass alltid kommer att ärvas och aldrig instansieras, kan jag typa den generiska klassen när jag ärver den, automatiskt uppdatera den generiska typen på de statiska metoderna?

Svar #1

Finns det något liknande abstrakta metoder som jag kan använda så att kompilatorn varnar mig om saknade metoder

Inte inbyggd, så du kommer inte att få bra fel bara genom att

// WILL NOT COMPILE. SAMPLE
class Foo {
    abstract static X() { } 
}

class Bar extends Foo { // Error here please
}

Du kan dock använda knep som typkompatibilitet för att säkerställa:

interface FooAbstract {
    X(): any;
}
let _ensureAbstractMatch: FooAbstract;
class Foo {
}

class Bar extends Foo {
}
_ensureAbstractMatch = Bar; // Error missing method

Exempelimplementation:

interface FooAbstract {
    X(): any;
}
let _ensureAbstractMatch: FooAbstract;
class Foo {
}

class Bar extends Foo {
    static X() { }
}
_ensureAbstractMatch = Bar; // OKAY

Svar #2

Jag vet att TypeScript inte stöder abstrakta statiska metoder. Finns det något liknande [statisk] abstrakt metod jag skulle kunna använda ...?

Forskning inom abstrakt statik också. Svaret är abstract static-deklaration stöds ännu inte, men du kan göra något liknande: Kräv att klassobjektet implementerar en abstrakt klass vilket är ekvivalent med att säga att dess statiska delar måste implementera gränssnittet. Exempel som tar några delar jag lärt mig från några andra svar 1, 2, 3:

// The static interface.
abstract class ResourceContainer { 
  abstract create(resource: Creator): Resource
  abstract count(query?: Query): number
  abstract find(query: Query): FindResult
}
// The instance interface.
abstract class Resource { 
  abstract read(): ReadResult
  abstract update(resource: Updator): ReadResult
  abstract delete(): void
}
// Attach newable to type corresponding to a concrete class - because Typescript ...
type NewableResource = ({new (...args: any): any} & ResourceContainer) 

const FooResource: NewableResource = class extends Resource {
  static create(resource: Creator): Resource { console.debug('create'); return new this(); }
  static find(query?: Query) { console.debug('find'); }
  static count(query?: Query) { console.debug('count'); return 0; }
  read() { console.debug('read'); }
  update(resource: Updator) { console.debug('update'); }
  delete() { console.debug('delete'); }
}

// TODO: flesh out these utility types ..
type Query = unknown;
type FindResult = unknown;
type ReadResult = unknown;
type Updator = unknown;
type Creator = unknown;

// Get the *type* of an class object *value*.
type FooResource = InstanceType<typeof FooResource> 

const x: FooResource = FooResource.create({});
x.read()

Alternativt, om du är så benägen, kan du bara göra så här:

abstract class Resource {
  static create(resource: Creator): Resource { throw NotImplementedError() }
  static count(query?: Query): number { throw NotImplementedError() }
  static find(query: Query): FindResult { throw NotImplementedError() }
  abstract read(): ReadResult
  abstract update(resource: Updator): ReadResult
  abstract delete(): void
}

Relaterade artiklar