Як визначити статичну властивість у інтерфейсі TypeScript


122

Я просто хочу оголосити статичну властивість в інтерфейсі typecript ? Я ніде про це не знайшов.

interface myInterface {
  static Name:string;
}

Це можливо?


Що ви точно намагаєтеся моделювати?
— Райан Кавано

5
Подумайте про використання класу "Foo Implements Bar", де Bar також є класом. Ви можете «впроваджувати» заняття іншими класами в TS.
— Тобіас Куднік

Відповіді:


46

Ви не можете визначити статичну властивість в інтерфейсі в TypeScript.

Скажімо, ви хотіли змінити Dateоб'єкт, а не намагатися додати його до визначень Date, ви можете обернути його або просто створити свій багатий клас дати, щоб робити речі, які Dateне роблять.

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

Оскільки Date є інтерфейсом в TypeScript, ви не можете розширити його класом за допомогою extendsключового слова, що шкода, оскільки це було б хорошим рішенням, якщо дата була б класом.

Якщо ви хочете розширити об’єкт Date для надання MinValueвластивості прототипу, ви можете:

interface Date {
    MinValue: Date;
}

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

Викликається за допомогою:

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

І якщо ви хочете зробити це доступним без примірника, ви також можете ... але це трохи суєтно.

interface DateStatic extends Date {
    MinValue: Date;
}

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

Викликається за допомогою:

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

1
@Rajagopal Щоб зрозуміти, ви можете фактично розширити інтерфейси в TS за допомогою extendsключового слова. Ви просто не можете розширити інтерфейс з класом (що вам потрібно зробити, щоб додати статичну властивість).
— Джуд Фішер

Стів - "Оскільки Дата - це інтерфейс у TypeScript, ви не можете розширити його за допомогою ключового слова extends" - це не так, чи не так?
— Джуд Фішер

1
Ви можете розширити інтерфейс, але ви не можете розширити його класом, лише реалізуйте його. Я цього не зрозумів.
— Фентон

Я б другий на думку @Nikos. Чи не успадковування інтерфейсів не перешкоджає меті підвищення читабельності, отже, зменшенню ремонтопридатності? Чи є хтось, хто міг би дати раціональне питання, чому використовувати успадкування інтерфейсів замість нового?
— Томаш Гессе

@TomasHesse немає жодної правильної відповіді на це (перше) питання. Ці рішення є компромісом між конкуруючими вимогами і правильною відповіддю є оцінка компромісу у відповідному контексті.
— Фентон

100

Дотримуйтесь відповіді @ Дункана @ Бартвдса, щоб тут було зрозуміло, як пройдуть роки.

На цей момент після виходу Typescript 1.5 (@ 15 червня 1515 р.) Ваш корисний інтерфейс

interface MyType {
    instanceMethod();
}

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

можна реалізувати таким чином за допомогою декоратора.

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

Зверніться до мого коментаря у відкритому номері 13462 github .

візуальний результат: помилка компіляції з відсутньою підказкою про статичний метод. введіть тут опис зображення

Після впровадження статичного методу підказка про відсутність методу. введіть тут опис зображення

Компіляція проходила після того, як був виконаний і статичний, і звичайний інтерфейс. введіть тут опис зображення


5
Ваша відповідь - відповідь. Дуже дякую. Робота з машинописом 2.4.1
— Хуан Каміло Гуарін Р

Ця відповідь не працює для мене в
— Typescript

2
@mmmeff Я тестував з 3.0.1 і він працює. Що означає «не працює»? Чи є повідомлення про помилку?
— Вал

1
@Chklang Дякую! Я оновив свою відповідь вашою пропозицією
— Валь,

1
Як ми можемо це зробити з абстрактним класом?
— geoyws

16

Ви можете нормально визначити інтерфейс:

interface MyInterface {
    Name:string;
}

але ти не можеш просто зробити

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

Щоб висловити, що клас повинен дотримуватися цього інтерфейсу для його статичних властивостей, вам потрібно трохи хитрощів:

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

Ви навіть можете зберегти назву класу, TypeScript (2.0) не буде проти:

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

Якщо ви хочете статично успадкувати багато інтерфейсів, вам доведеться спочатку об'єднати їх у новий:

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
}

Або якщо ви не хочете називати об'єднаний інтерфейс, ви можете зробити:

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
}

Робочий приклад


Я отримую помилку TS2322: Анонімний клас не можна привласнювати
— JoshuaDavid

@FireCoding Яку версію TypeScript ви використовуєте? Мої приклади працювали з 2.0
— Каміль Шот

Це більш просте рішення, ніж декоратор на замовлення @staticImplements.
— Роберт Пеннер

1
Я думаю, що це чудове рішення. для статичних методів асинхронізації ви також можете використовуватиreply(msg: IBotMsg): Promise<IBotReply>
— dcsan

чи є спосіб визначити і статичні, і методи екземплярів, або всі методи тепер статичні?
— dcsan

15

Статичні властивості зазвичай розміщуються на (глобальному) конструкторі для об'єкта, тоді як ключове слово "інтерфейс" застосовується до примірників об'єкта.

Попередня відповідь, безумовно, правильна, якщо ви пишете клас у TypeScript. Це може допомогти іншим зрозуміти, що якщо ви описуєте об'єкт, який вже реалізований в іншому місці, то глобальний конструктор, що включає статичні властивості, може бути оголошений так:

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

редагувати: розміщено як повну відповідь нижче
— Bartvds

На даний момент після випуску Typescript 1.5 (6.15.2015), тут є дієвим способом опису статичних членів класу. stackoverflow.com/a/43674389/681830
— Val

8

Статичні модифікатори не можуть з’являтися на типі елемента (помилка TypeScript TS1070). Ось чому я рекомендую використовувати абстрактний клас для вирішення місії:

Приклад

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

Так, це відповідь!
— vintproykt

7

Рішення @ duncan, вказане вище new()для статичного типу, також працює з інтерфейсами:

interface MyType {
    instanceMethod();
}

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

Який із них би мій клас implement?
— Денис Пшенов

2
На даний момент ви не можете використовувати інтерфейс для опису статичних членів, лише членів екземпляра. Отже, у цьому прикладі ваш клас буде реалізований MyType(як і в class Foo implements MyType). Статичний інтерфейс реально корисний лише у визначеннях при описі існуючого коду JS.
— Бартвдс

На даний момент після випуску Typescript 1.5 (6.15.2015), тут є дієвим способом опису статичних членів класу. stackoverflow.com/a/43674389/681830
— Val

4

Якщо ви хочете визначити статичний клас (тобто всі методи / властивості є статичними), ви можете зробити щось подібне:

interface MyStaticClassInterface {
  foo():string;
}

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

У цьому випадку статичний "клас" насправді є просто звичайним об'єктом-ol'-js, який реалізує всі методи MyStaticClassInterface


4

Так, можливо. Ось рішення

export interface Foo {

    test(): void;
}

export namespace Foo {

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

}

Це круто! Саме те, що мені було потрібно
— Умар Фарук

1
@UmarFarooqKhawaja Я радий допомогти!
— Pavel_K

значить, це використовується простір імен Fooдля визначення визначення класу? де Class Foo буде реалізовувати statMethod (), це здається трохи злому, особливо, оскільки простори імен зараз
— винищені

@dcsan Так, це трохи хак. Але що робити? Дуже багато питань машинопису відкрито багато років з етикеткою waiting more feedback. Тож ви або чекаєте, поки статичні методи будуть додані в інтерфейс, або зробите це так.
— Pavel_K

але також я не дуже розумію це. export namespaceНасправді ваша реалізація класу? Це, здається, дуже згинає речі. Цікаво, що ще, що ламається в Класах?
— dcsan

3

Інший варіант, який не згаданий тут, - це визначення змінної з типом, що представляє статичний інтерфейс, і призначення йому виразу класу:

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

Ефект такий же, як у відповіді з декораторами , але без накладних витрат декораторів

Ігровий майданчик

Відповідна пропозиція / дискусія на GitHub



2

Я знайшов спосіб зробити це (без декораторів) для мого конкретного випадку використання.

Важлива частина , яка перевіряє наявність статичних членів є IObjectClassі використання cls: IObjectClass<T>в createObjectметоді:

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

Хоча статичне ключове слово не підтримується в інтерфейсі в Typescript, але ми можемо досягти цього, створивши function interfaceстатичний член.

У своєму наступному коді я створив функціональний інтерфейс, Factoryякий має два статичних члена serialNumberта 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

Я реалізував таке рішення, як Каміл Шот , і воно має небажаний ефект. У мене недостатньо репутації, щоб розмістити це як коментар, тому я публікую його тут, якщо хтось спробує це рішення і прочитає це.

Рішення таке:

interface MyInterface {
    Name: string;
}

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

Однак використання виразу класу не дозволяє мені використовувати MyClassяк тип. Якщо я напишу щось на кшталт:

const myInstance: MyClass;

myInstanceвиявляється типу any, і мій редактор показує таку помилку:

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

В кінцевому підсумку втрачаю важливішу типізацію, ніж ту, яку хотів досягти за допомогою інтерфейсу для статичної частини класу.

Розв’язання Валя за допомогою декоратора дозволяє уникнути цієї неприємності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.