Как определить статическое свойство в интерфейсе TypeScript


122

Я просто хочу объявить статическое свойство в интерфейсе машинописного текста ? Я нигде не нашел по этому поводу.

interface myInterface {
  static Name:string;
}

Является ли это возможным?


Что именно вы пытаетесь моделировать?
— Райан Кавано,

5
Рассмотрите возможность использования «класс Foo реализует Bar», где Bar также является классом. Вы можете «реализовать» классы другими классами в TS.
— Тобиас Кадник

Ответы:


46

Вы не можете определить статическое свойство интерфейса в TypeScript.

Допустим, вы хотите изменить Dateобъект, вместо того, чтобы пытаться добавить к определениям Date, вы можете обернуть его или просто создать свой богатый класс даты, чтобы делать то, чего Dateне делает.

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

Поскольку Date - это интерфейс в TypeScript, вы не можете расширить его классом с помощью extendsключевого слова, что немного обидно, поскольку это было бы хорошим решением, если бы date была классом.

Если вы хотите расширить объект 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ключевого слова. Вы просто не можете расширить интерфейс классом (что вам нужно сделать, чтобы добавить статическое свойство).
— Джуд Фишер

Стив: «Поскольку Date - это интерфейс в TypeScript, вы не можете расширить его с помощью ключевого слова extends» - это неправильно, не так ли?
— Джуд Фишер

1
Вы можете расширить интерфейс, но не можете расширить его классом, а только реализовать. Я не дал этого ясно.
— Fenton

Я бы поддержал мнение @Nikos. Разве наследование интерфейсов не противоречит цели повышения читабельности и, следовательно, снижения ремонтопригодности? Есть ли кто-нибудь, кто мог бы объяснить, почему использовать наследование интерфейсов вместо нового?
— Tomas Hesse

@TomasHesse нет единого правильного ответа на этот (первый) вопрос. Эти решения представляют собой компромисс между конкурирующими требованиями, и правильный ответ - оценить компромисс в соответствующем контексте.
— Fenton

100

Следуйте ответу @Duncan @ Bartvds здесь, чтобы предоставить работоспособный способ по прошествии лет.

На данный момент после выпуска Typescript 1.5 (@ 15 июня 2015 г.) ваш полезный интерфейс

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

См. Мой комментарий в github open issue 13462 .

Визуальный результат: Ошибка компиляции с отсутствующим намеком на статический метод. введите описание изображения здесь

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

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


5
Ваш ответ - это ответ. Огромное спасибо. Работа с машинописным текстом 2.4.1
— Хуан Камило Гуарин П.

Этот ответ не работает для меня в Typescript 3.0.1
— mmmeff

2
@mmmeff Я тестировал 3.0.1, и он работает. Что значит «не работает»? Есть ли сообщение об ошибке?
— Val

1
@Chklang Спасибо! Я обновил свой ответ вашим предложением
— Вэл

1
Как мы можем сделать это с помощью абстрактного класса?
— geoyws 01

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: Анонимный класс не может быть назначен типу
— Джошуа Дэвид

@FireCoding Какую версию TypeScript вы используете? Мои примеры работали с 2.0
— Камил

Это более простое решение, чем пользовательский декоратор @staticImplements.
— Роберт Пеннер

1
Думаю, это отличное решение. для асинхронных статических методов вы также можете использоватьreply(msg: IBotMsg): Promise<IBotReply>
— dcsan

есть ли способ определить как статические методы, так и методы экземпляра, или все методы теперь статичны?
— dcsan 04

15

Статические свойства обычно помещаются в (глобальный) конструктор объекта, тогда как ключевое слово «interface» применяется к экземплярам объекта.

Предыдущий ответ, конечно, правильный, если вы пишете класс на 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());

Ага, вот и ответ!
— винтпройт

7

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

interface MyType {
    instanceMethod();
}

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

Какой из них подойдет моему классу implement?
— Денис Пшенов

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

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

4

Если вы хотите определить статический класс (т.е. все методы / свойства статичны), вы можете сделать что-то вроде этого:

interface MyStaticClassInterface {
  foo():string;
}

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

В этом случае статический «класс» на самом деле просто простой-ол'-js-объект, который реализует все методы MyStaticClassInterface


4

Да, это возможно. Вот решение

export interface Foo {

    test(): void;
}

export namespace Foo {

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

}

Это круто! Именно то, что мне было нужно
— Умар Фарук Хаваджа

1
@UmarFarooqKhawaja Я рад помочь!
— Pavel_K 04

Значит, здесь используется пространство имен Fooвместо определения класса? где Class Foo будет реализовывать statMethod (), это кажется чем-то вроде взлома, особенно, поскольку пространства имен сейчас вроде как устарели ...
— dcsan

@dcsan Да, это что-то вроде взлома. Но что делать? Многие выпуски машинописного текста открыты на протяжении многих лет с этикеткой waiting more feedback. Итак, вы либо ждете, пока статические методы не будут добавлены в интерфейс, либо сделаете это так.
— Pavel_K 04

но также я не совсем понимаю .. это на export namespaceсамом деле ваша реализация класса? Кажется, это действительно сильно меняет ситуацию. Интересно, что еще ломается в классах?
— dcsan 04

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

Хотя ключевое слово static не поддерживается в интерфейсе в 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.