Jak zdefiniować właściwość statyczną w interfejsie TypeScript


122

Chcę tylko zadeklarować właściwość statyczną w interfejsie maszynopisu ? Nie znalazłem nigdzie w związku z tym.

interface myInterface {
  static Name:string;
}

Czy to możliwe?


Co dokładnie próbujesz wymodelować?
— Ryan Cavanaugh

5
Rozważ użycie „class Foo implements Bar”, gdzie Bar jest również klasą. Możesz „implementować” klasy przez inne klasy w TS.
— Tobias Cudnik,

Odpowiedzi:


46

Nie można zdefiniować właściwości statycznej w interfejsie w TypeScript.

Załóżmy, że chcesz zmienić Dateobiekt, zamiast próbować dodawać go do definicji Date, możesz go opakować lub po prostu utworzyć klasę bogatej daty, aby robić rzeczy, które Datenie robią.

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

Ponieważ Date jest interfejsem w TypeScript, nie można go rozszerzyć o klasę za pomocą extendssłowa kluczowego, co jest trochę wstydem, ponieważ byłoby to dobre rozwiązanie, gdyby date było klasą.

Jeśli chcesz przedłużyć obiekt Date, aby udostępnić MinValuewłaściwość prototypowi, możesz:

interface Date {
    MinValue: Date;
}

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

Zadzwoniono za pomocą:

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

A jeśli chcesz udostępnić go bez instancji, możesz też ... ale jest to trochę wybredne.

interface DateStatic extends Date {
    MinValue: Date;
}

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

Zadzwoniono za pomocą:

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

1
@Rajagopal Aby było jasne, możesz faktycznie rozszerzać interfejsy w TS używając extendssłowa kluczowego. Po prostu nie możesz rozszerzyć interfejsu o klasę (co musiałbyś zrobić, aby dodać właściwość statyczną).
— Jude Fisher

Steve - „Ponieważ Date jest interfejsem w TypeScript, nie możesz go rozszerzyć za pomocą słowa kluczowego extends” - to nieprawda, prawda?
— Jude Fisher,

1
Możesz rozszerzyć interfejs, ale nie możesz go rozszerzyć o klasę, tylko zaimplementuj. Nie wyjaśniłem tego jasno.
— Fenton,

Poparłbym opinię @Nikos. Czy dziedziczenie interfejsów nie pokonuje celu zwiększenia czytelności, a tym samym zmniejszenia łatwości konserwacji? Czy jest ktoś, kto mógłby wytłumaczyć, dlaczego należy używać dziedziczenia interfejsów zamiast nowego?
— Tomas Hesse

@TomasHesse nie ma jednej poprawnej odpowiedzi na to (pierwsze) pytanie. Decyzje te są kompromisem między konkurującymi żądaniami, a właściwą odpowiedzią jest ocena kompromisu w odpowiednim kontekście.
— Fenton

100

Postępuj zgodnie z odpowiedzią @ Duncan's @ Bartvds, aby zapewnić praktyczny sposób po upływie lat.

W tym momencie po wydaniu Typescript 1.5 (@ Jun 15 '15), Twój pomocny interfejs

interface MyType {
    instanceMethod();
}

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

można w ten sposób zrealizować przy pomocy dekoratora.

/* 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() {}
}

Zobacz mój komentarz na github open issue 13462 .

wynik wizualny: błąd kompilacji z brakiem śladu metody statycznej. wprowadź opis obrazu tutaj

Po zaimplementowaniu metody statycznej wskazówka dotycząca metody brakującej. wprowadź opis obrazu tutaj

Kompilacja przeszła po spełnieniu zarówno statycznego, jak i normalnego interfejsu. wprowadź opis obrazu tutaj


5
Twoja odpowiedź jest odpowiedzią. Dziękuję bardzo. Praca z maszynopisem 2.4.1
— Juan Camilo Guarin P

Ta odpowiedź nie działa dla mnie w
— Typescript

2
@mmmeff Testowałem z 3.0.1 i działa. Co masz na myśli mówiąc „nie działa”? Czy jest jakiś komunikat o błędzie?
— Val

1
@Chklang Dziękuję! Zaktualizowałem moją odpowiedź Twoją sugestią
— Val

1
Jak możemy to zrobić z klasą abstrakcyjną?
— geoyws

16

Możesz normalnie zdefiniować interfejs:

interface MyInterface {
    Name:string;
}

ale nie możesz tak po prostu zrobić

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

Aby wyrazić, że klasa powinna podążać za tym interfejsem ze względu na swoje właściwości statyczne, potrzebujesz trochę sztuczki:

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

Możesz nawet zachować nazwę klasy, TypeScript (2.0) nie będzie miał nic przeciwko:

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

Jeśli chcesz dziedziczyć z wielu interfejsów statycznie, musisz najpierw scalić je w nowy:

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
}

Lub jeśli nie chcesz nazywać scalonego interfejsu, możesz zrobić:

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
}

Przykład roboczy


Występuje błąd TS2322: Nie można przypisać klasy anonimowej do typu
— JoshuaDavid

@FireCoding Której wersji języka TypeScript używasz? Moje przykłady działały z 2.0
— Kamil Szot

Jest to prostsze rozwiązanie niż niestandardowy dekorator @staticImplements.
— Robert Penner,

1
Myślę, że to świetne rozwiązanie. dla asynchronicznych metod statycznych można również użyćreply(msg: IBotMsg): Promise<IBotReply>
— dcsan

czy istnieje sposób zdefiniowania zarówno metod statycznych, jak i instancji, czy też wszystkie metody są teraz tylko statyczne?
— dcsan,

15

Właściwości statyczne są zwykle umieszczane w (globalnym) konstruktorze obiektu, podczas gdy słowo kluczowe „interface” odnosi się do instancji obiektu.

Poprzednia udzielona odpowiedź jest oczywiście poprawna, jeśli piszesz klasę w języku TypeScript. Innym może pomóc wiedzieć, że jeśli opisujesz obiekt, który został już zaimplementowany w innym miejscu, to globalny konstruktor zawierający właściwości statyczne można zadeklarować w następujący sposób:

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

edycja: opublikowana jako pełna odpowiedź poniżej
— Bartvds

W tym momencie po wydaniu Typescript 1.5 (6.15.2015), tutaj jest praktyczny sposób na opisanie statycznych członków klasy. stackoverflow.com/a/43674389/681830
— Val

8

Modyfikatory statyczne nie mogą pojawić się w elemencie typu (błąd TypeScript TS1070). Dlatego do rozwiązania misji polecam użyć klasy abstrakcyjnej:

Przykład

// 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());

Tak, to jest odpowiedź!
— vintproykt

7

Rozwiązanie @ duncan powyżej, określając new()typ statyczny, działa również z interfejsami:

interface MyType {
    instanceMethod();
}

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

Który z nich byłby moją klasą implement?
— Denis Pshenov

2
W tym momencie nie można używać interfejsu do opisywania elementów statycznych, tylko elementy składowe instancji. Więc w tym przykładzie twoja klasa zaimplementowałaby MyType(jak w class Foo implements MyType). Interfejs statyczny jest naprawdę przydatny tylko w definicjach, podczas opisywania istniejącego kodu JS.
— Bartvds,

W tym momencie po wydaniu Typescript 1.5 (6.15.2015), tutaj jest praktyczny sposób na opisanie statycznych członków klasy. stackoverflow.com/a/43674389/681830
— Val

4

Jeśli chcesz zdefiniować klasę statyczną (tj. Wszystkie metody / właściwości są statyczne), możesz zrobić coś takiego:

interface MyStaticClassInterface {
  foo():string;
}

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

W tym przypadku statyczna „klasa” jest w rzeczywistości zwykłym obiektem-ol'-js, który implementuje wszystkie metody MyStaticClassInterface


4

Tak to mozliwe. Oto rozwiązanie

export interface Foo {

    test(): void;
}

export namespace Foo {

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

}

To jest niesamowite! Dokładnie to, czego potrzebowałem
— Umar Farooq Khawaja

1
@UmarFarooqKhawaja Cieszę się, że mogę pomóc!
— Pavel_K

więc to jest używanie przestrzeni nazw Foodo zastępowania definicji klasy? gdzie Class Foo zaimplementowałaby statMethod (), wydaje się to trochę hackem, szczególnie, ponieważ przestrzenie nazw są teraz trochę przestarzałe ...
— dcsan

@dcsan Tak, to trochę hack. Ale co robić? Wiele numerów maszynopisu jest otwartych przez wiele lat z etykietą waiting more feedback. Więc albo poczekaj, aż statyczne metody zostaną dodane do interfejsu, albo zrób w ten sposób.
— Pavel_K,

ale też nie do końca rozumiem .. czy export namespacefaktycznie jest implementacja Twojej klasy? To wydaje się bardzo naginać sprawy. Zastanawiam się, co jeszcze się psuje w klasach?
— dcsan,

3

Inną opcją niewymienioną tutaj jest zdefiniowanie zmiennej o typie reprezentującym interfejs statyczny i przypisanie do niej wyrażenia klasowego:

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() { }
}

Efekt jest taki sam jak w przypadku odpowiedzi z dekoratorami , ale bez narzutu dekoratorów

Plac zabaw

Odpowiednia sugestia / dyskusja na GitHub



2

Znalazłem sposób, aby to zrobić (bez dekoratorów) dla mojego konkretnego przypadku użycia.

Ważną częścią sprawdzającą statyczne elementy członkowskie jest IObjectClassi używana cls: IObjectClass<T>w createObjectmetodzie:

//------------------------
// 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

Chociaż statyczne słowo kluczowe nie jest obsługiwane w interfejsie w Typescript, ale możemy to osiągnąć, tworząc element, function interfacektóry ma statyczny element członkowski.

W poniższym kodzie utworzyłem interfejs funkcji, Factoryktóry ma dwa statyczne elementy członkowskie serialNumberi printSerial .

// 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

Wdrożyłem takie rozwiązanie jak Kamil Szot i ma to niepożądany efekt. Nie mam wystarczającej reputacji, aby opublikować to jako komentarz, więc zamieszczam go tutaj na wypadek, gdyby ktoś próbował tego rozwiązania i czytał.

Rozwiązaniem jest:

interface MyInterface {
    Name: string;
}

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

Jednak użycie wyrażenia klasy nie pozwala mi używać MyClassjako typu. Jeśli napiszę coś takiego:

const myInstance: MyClass;

myInstanceokazuje się być typowy any, a mój edytor wyświetla następujący błąd:

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

Ostatecznie tracę ważniejsze pisanie niż to, które chciałem osiągnąć z interfejsem dla statycznej części klasy.

Rozwiązanie Val wykorzystujące dekorator pozwala uniknąć tej pułapki.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.