So definieren Sie statische Eigenschaften in der TypeScript-Oberfläche


122

Ich möchte nur eine statische Eigenschaft in der Typoskript- Schnittstelle deklarieren . Ich habe diesbezüglich nichts gefunden.

interface myInterface {
  static Name:string;
}

Ist es möglich?


Was versuchst du genau zu modellieren?
— Ryan Cavanaugh

5
Erwägen Sie die Verwendung von 'Klasse Foo implementiert Bar', wobei Bar auch eine Klasse ist. Sie können Klassen von anderen Klassen in TS 'implementieren'.
— Tobias Cudnik

Antworten:


46

Sie können in TypeScript keine statische Eigenschaft für eine Schnittstelle definieren.

Angenommen, Sie möchten das DateObjekt ändern , anstatt zu versuchen, es zu den Definitionen von hinzuzufügen. DateSie können es umbrechen oder einfach Ihre Rich-Date-Klasse erstellen, um die Aufgaben auszuführen, Datedie nicht ausgeführt werden.

class RichDate {
    public static MinValue = new Date();
}

Da Date eine Schnittstelle in TypeScript ist, können Sie es nicht mit einer Klasse mit dem extendsSchlüsselwort erweitern, was etwas schade ist, da dies eine gute Lösung wäre, wenn date eine Klasse wäre.

Wenn Sie das Date-Objekt erweitern möchten, um eine MinValueEigenschaft für den Prototyp bereitzustellen , können Sie:

interface Date {
    MinValue: Date;
}

Date.prototype.MinValue = new Date(0);

Aufgerufen mit:

var x = new Date();
console.log(x.MinValue);

Und wenn Sie es ohne Instanz verfügbar machen möchten, können Sie es auch ... aber es ist ein bisschen pingelig.

interface DateStatic extends Date {
    MinValue: Date;
}

Date['MinValue'] = new Date(0);

Aufgerufen mit:

var x: DateStatic = <any>Date; // We aren't using an instance
console.log(x.MinValue);

1
@Rajagopal Um klar zu sein, können Sie Schnittstellen in TS mithilfe des extendsSchlüsselworts erweitern. Sie können eine Schnittstelle einfach nicht mit einer Klasse erweitern (was Sie tun müssten, um eine statische Eigenschaft hinzuzufügen).
— Jude Fisher

Steve - 'Da Date eine Schnittstelle in TypeScript ist, können Sie es nicht mit dem Schlüsselwort extenses erweitern' - das ist nicht richtig, oder?
— Jude Fisher

1
Sie können die Schnittstelle erweitern, aber Sie können sie nicht mit einer Klasse erweitern, sondern nur implementieren. Das habe ich nicht klar gemacht.
— Fenton

Ich würde @Nikos Meinung zustimmen. Verstößt die Vererbung von Schnittstellen nicht gegen den Zweck, die Lesbarkeit zu verbessern und damit die Wartbarkeit zu verringern? Gibt es jemanden, der eine Begründung geben könnte, warum die Vererbung von Schnittstellen anstelle einer neuen verwendet wird?
— Tomas Hesse

@TomasHesse gibt es keine einzige richtige Antwort auf diese (erste) Frage. Diese Entscheidungen sind Kompromisse zwischen konkurrierenden Anforderungen, und die richtige Antwort besteht darin, den Kompromiss im geeigneten Kontext zu bewerten.
— Fenton

98

Folgen Sie der Antwort von @ Duncan @ Bartvds, um nach Jahren einen praktikablen Weg zu finden.

An diesem Punkt nach der Veröffentlichung von Typescript 1.5 (@Jun 15 '15) ist Ihre hilfreiche Oberfläche

interface MyType {
    instanceMethod();
}

interface MyTypeStatic {
    new():MyType;
    staticMethod();
}

kann auf diese Weise mit Hilfe des Dekorateurs implementiert werden.

/* class decorator */
function staticImplements<T>() {
    return <U extends T>(constructor: U) => {constructor};
}

@staticImplements<MyTypeStatic>()   /* this statement implements both normal interface & static interface */
class MyTypeClass { /* implements MyType { */ /* so this become optional not required */
    public static staticMethod() {}
    instanceMethod() {}
}

Siehe meinen Kommentar bei Github Open Issue 13462 .

visuelles Ergebnis: Kompilierungsfehler mit einem Hinweis auf fehlende statische Methode. Geben Sie hier die Bildbeschreibung ein

Nach der Implementierung der statischen Methode fehlt ein Hinweis auf die fehlende Methode. Geben Sie hier die Bildbeschreibung ein

Die Kompilierung wurde bestanden, nachdem sowohl die statische als auch die normale Schnittstelle erfüllt waren. Geben Sie hier die Bildbeschreibung ein


5
Ihre Antwort ist DIE Antwort. Ich danke dir sehr. Arbeiten mit Typoskript 2.4.1
— Juan Camilo Guarin P

Diese Antwort funktioniert nicht für mich in Typescript 3.0.1
— mmmeff

2
@mmmeff Ich habe mit 3.0.1 getestet und es funktioniert. Was meinst du mit "nicht arbeiten"? Gibt es eine Fehlermeldung?
— Val

1
@Chklang Danke! Ich habe meine Antwort mit Ihrem Vorschlag aktualisiert
— Val

1
Wie können wir das mit einer Abstract-Klasse machen?
— Geoyws

16

Sie können die Schnittstelle normal definieren:

interface MyInterface {
    Name:string;
}

aber du kannst es nicht einfach tun

class MyClass implements MyInterface {
    static Name:string; // typescript won't care about this field
    Name:string;         // and demand this one instead
}

Um auszudrücken, dass eine Klasse aufgrund ihrer statischen Eigenschaften dieser Schnittstelle folgen sollte, müssen Sie einige Tricks ausführen:

var MyClass: MyInterface;
MyClass = class {
    static Name:string; // if the class doesn't have that field it won't compile
}

Sie können sogar den Namen der Klasse behalten, TypeScript (2.0) hat nichts dagegen:

var MyClass: MyInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that field it won't compile
}

Wenn Sie statisch von vielen Schnittstellen erben möchten, müssen Sie diese zuerst zu einer neuen zusammenführen:

interface NameInterface {
    Name:string;
}
interface AddressInterface {
    Address:string;
}
interface NameAndAddressInterface extends NameInterface, AddressInterface { }
var MyClass: NameAndAddressInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that static field code won't compile
    static Address:string; // if the class doesn't have that static field code won't compile
}

Oder wenn Sie die zusammengeführte Schnittstelle nicht benennen möchten, können Sie Folgendes tun:

interface NameInterface {
    Name:string;
}
interface AddressInterface {
    Address:string;
}
var MyClass: NameInterface & AddressInterface;
MyClass = class MyClass {
    static Name:string; // if the class doesn't have that static field code won't compile
    static Address:string; // if the class doesn't have that static field code won't compile
}

Arbeitsbeispiel


Ich erhalte die Fehlermeldung TS2322: Anonyme Klasse kann nicht dem Typ
— zugewiesen werden

@FireCoding Welche TypeScript-Version verwenden Sie? Meine Beispiele arbeiteten mit 2.0
— Kamil Szot

Dies ist eine einfachere Lösung als der benutzerdefinierte Dekorateur @staticImplements.
— Robert Penner

1
Ich denke, das ist eine großartige Lösung. für asynchrone statische Methoden können Sie auch verwendenreply(msg: IBotMsg): Promise<IBotReply>
— dcsan

Gibt es eine Möglichkeit, sowohl statische als auch Instanzmethoden zu definieren, oder sind alle Methoden jetzt nur noch statisch?
— dcsan

15

Statische Eigenschaften werden normalerweise im (globalen) Konstruktor für das Objekt platziert, während das Schlüsselwort "interface" für Instanzen des Objekts gilt.

Die vorherige Antwort ist natürlich richtig, wenn Sie die Klasse in TypeScript schreiben. Es kann anderen helfen zu wissen, dass, wenn Sie ein Objekt beschreiben, das bereits an anderer Stelle implementiert ist, der globale Konstruktor einschließlich der statischen Eigenschaften wie folgt deklariert werden kann:

declare var myInterface : {
  new(): Interface;
  Name:string;
}

bearbeiten: als vollständige Antwort unten
— Bartvds

An diesem Punkt nach der Veröffentlichung von Typescript 1.5 (6.15.2015) ist es hier eine praktikable Möglichkeit, statische Mitglieder einer Klasse zu beschreiben. stackoverflow.com/a/43674389/681830
— Val

8

Statische Modifikatoren können auf einem Typelement nicht angezeigt werden (TypeScript-Fehler TS1070). Deshalb empfehle ich, eine abstrakte Klasse zu verwenden, um die Mission zu lösen:

Beispiel

// Interface definition
abstract class MyInterface {
  static MyName: string;
  abstract getText(): string;
}

// Interface implementation
class MyClass extends MyInterface {
  static MyName = 'TestName';
  getText(): string {
    return `This is my name static name "${MyClass.MyName}".`;
  }
}

// Test run
const test: MyInterface = new MyClass();
console.log(test.getText());

Ja, das ist die Antwort!
— vintproykt

7

Die oben angegebene Lösung von @ duncan new()für den statischen Typ funktioniert auch mit Schnittstellen:

interface MyType {
    instanceMethod();
}

interface MyTypeStatic {
    new():MyType;
    staticMethod();
}

Welches davon würde meine Klasse implement?
— Denis Pshenov

2
Derzeit können Sie keine Schnittstelle verwenden, um statische Elemente zu beschreiben, sondern nur Instanzmitglieder. In diesem Beispiel würde Ihre Klasse MyType(wie in class Foo implements MyType) implementieren . Die statische Schnittstelle ist nur in Definitionen wirklich nützlich, wenn vorhandener JS-Code beschrieben wird.
— Bartvds

An diesem Punkt nach der Veröffentlichung von Typescript 1.5 (6.15.2015) ist es hier eine praktikable Möglichkeit, statische Mitglieder einer Klasse zu beschreiben. stackoverflow.com/a/43674389/681830
— Val

4

Wenn Sie eine statische Klasse definieren möchten (dh alle Methoden / Eigenschaften sind statisch), können Sie Folgendes tun:

interface MyStaticClassInterface {
  foo():string;
}

var myStaticClass:MyStaticClassInterface = {
  foo() {
    return 'bar';
  }
};

In diesem Fall ist die statische "Klasse" wirklich nur ein einfaches-ol'-js-Objekt, das alle Methoden von implementiert MyStaticClassInterface


4

Ja, es ist möglich. Hier ist die Lösung

export interface Foo {

    test(): void;
}

export namespace Foo {

    export function statMethod(): void {
        console.log(2);
    }

}

Das ist fantastisch! Genau das, was ich brauchte
— Umar Farooq Khawaja

1
@UmarFarooqKhawaja Ich bin froh zu helfen!
— Pavel_K

Verwenden Sie also einen Namespace, Fooum für eine Klassendefinition einzutreten? Wo Class Foo die statMethod () implementieren würde, scheint dies ein bisschen
— hackig

@dcsan Ja, es ist ein bisschen ein Hack. Aber was soll man machen? Viele Fragen des Typoskripts sind seit vielen Jahren mit Label offen waiting more feedback. Sie warten entweder, bis statische Methoden zur Schnittstelle hinzugefügt wurden, oder tun dies auf diese Weise.
— Pavel_K

aber ich verstehe es auch nicht ganz .. das export namespaceist eigentlich deine Klassenimplementierung? Das scheint die Dinge sehr zu verbiegen. Ich frage mich, was sonst noch in Klassen bricht?
— dcsan

3

Eine andere Option, die hier nicht erwähnt wird, besteht darin, eine Variable mit einem Typ zu definieren, der die statische Schnittstelle darstellt, und ihr einen Klassenausdruck zuzuweisen:

interface MyType {
    instanceMethod(): void;
}

interface MyTypeStatic {
    new(): MyType;
    staticMethod(): void;
}

// ok
const MyTypeClass: MyTypeStatic = class MyTypeClass {
    public static staticMethod() { }
    instanceMethod() { }
}

// error: 'instanceMethod' is missing
const MyTypeClass1: MyTypeStatic = class MyTypeClass {
    public static staticMethod() { }
}

// error: 'staticMethod' is missing
const MyTypeClass2: MyTypeStatic = class MyTypeClass {
    instanceMethod() { }
}

Der Effekt ist der gleiche wie bei der Antwort mit Dekorateuren , jedoch ohne Overhead von Dekorateuren

Spielplatz

Relevanter Vorschlag / Diskussion auf GitHub


2

Sie können die Schnittstelle mit dem Namespace unter demselben Namen zusammenführen:

interface myInterface { }

namespace myInterface {
  Name:string;
}

Diese Schnittstelle ist jedoch nur nützlich, um zu wissen, dass sie über eine Eigenschaft verfügt Name. Sie können es nicht implementieren.


2

Ich habe einen Weg gefunden, dies (ohne Dekorateure) für meinen speziellen Anwendungsfall zu tun.

Der wichtige Teil, der nach statischen Elementen sucht, ist IObjectClassund verwendet cls: IObjectClass<T>in der createObjectMethode:

//------------------------
// Library
//------------------------
interface IObject {
  id: number;
}
interface IObjectClass<T> {
  new(): T;
  table_name: string;
}
function createObject<T extends IObject>(cls: IObjectClass<T>, data:Partial<T>):T {
  let obj:T = (<any>Object).assign({},
    data,
    {
      id: 1,
      table_name: cls.table_name,
    }
  )
  return obj;
}

//------------------------
// Implementation
//------------------------
export class User implements IObject {
  static table_name: string = 'user';
  id: number;
  name: string;
}

//------------------------
// Application
//------------------------
let user = createObject(User, {name: 'Jimmy'});
console.log(user.name);

0

Obwohl das statische Schlüsselwort in der Schnittstelle in Typescript nicht unterstützt wird, können wir es erreichen, indem wir ein statisches Element mit statischem Element erstellen function interface.

In meinem folgenden Code habe ich eine Funktionsschnittstelle erstellt Factory, die zwei statische ElementeserialNumber und printSerial enthält .

// factory is a function interface
interface Factory<T> {
    (name: string, age: number): T;

    //staic property
    serialNumber: number;

    //static method
    printSrial: () => void;
}

class Dog {
    constructor(public name: string, public age: number) { }
}

const dogFactory: Factory<Dog> = (name, age) => {
    return new Dog(name, age);
}

// initialising static members

dogFactory.serialNumber = 1234;
dogFactory.printSrial = () => console.log(dogFactory.serialNumber);


//instance of Dog that DogFactory creates
const myDog = dogFactory("spike", 3);

//static property that returns 1234
console.log(dogFactory.serialNumber)

//static method that prints the serial 1234
dogFactory.printSrial();

0

Ich habe eine Lösung wie die von Kamil Szot implementiert , die einen unerwünschten Effekt hat. Ich habe nicht genug Ruf, um dies als Kommentar zu posten, also poste ich es hier, falls jemand diese Lösung versucht und dies liest.

Die Lösung ist:

interface MyInterface {
    Name: string;
}

const MyClass = class {
    static Name: string;
};

Die Verwendung eines Klassenausdrucks erlaubt mir jedoch nicht, ihn MyClassals Typ zu verwenden. Wenn ich so etwas schreibe wie:

const myInstance: MyClass;

myInstanceEs stellt sich heraus, dass es sich um einen Typ handelt any, und mein Editor zeigt den folgenden Fehler an:

'MyClass' refers to a value, but is being used as a type here. Did you mean 'typeof MyClass'?ts(2749)

Am Ende verliere ich eine wichtigere Eingabe als die, die ich mit der Schnittstelle für den statischen Teil der Klasse erreichen wollte.

Die Lösung von Val mit einem Dekorateur vermeidet diese Gefahr.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.