Como definir propriedade estática na interface TypeScript


122

Eu só quero declarar uma propriedade estática na interface typescript ? Eu não encontrei nada sobre isso.

interface myInterface {
  static Name:string;
}

É possível?


O que você está tentando modelar, exatamente?
Ryan Cavanaugh

5
Considere o uso de 'classe Foo implementa Bar', onde Bar também é uma classe. Você pode 'implementar' classes por outras classes no TS.
Tobias Cudnik

Respostas:


46

Você não pode definir uma propriedade estática em uma interface em TypeScript.

Digamos que você queira alterar o Dateobjeto, em vez de tentar adicionar às definições de Date, você pode embrulhá-lo ou simplesmente criar sua classe de data rica para fazer o que Datenão faz.

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

Como Date é uma interface em TypeScript, você não pode estendê-la com uma classe usando a extendspalavra - chave, o que é uma pena, pois seria uma boa solução se date fosse uma classe.

Se você deseja estender o objeto Date para fornecer uma MinValuepropriedade no protótipo, você pode:

interface Date {
    MinValue: Date;
}

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

Chamado usando:

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

E se você quiser torná-lo disponível sem uma instância, você também pode ... mas é um pouco complicado.

interface DateStatic extends Date {
    MinValue: Date;
}

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

Chamado usando:

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

1
@Rajagopal Para ser claro, você pode realmente estender interfaces no TS usando a extendspalavra - chave. Você simplesmente não pode estender uma interface com uma classe (o que você precisaria fazer para adicionar uma propriedade estática).
Jude Fisher de

Steve - 'Como Date é uma interface em TypeScript, você não pode estendê-la usando a palavra-chave extends' - não está certo, certo?
Jude Fisher de

1
Você pode estender a interface, mas não pode estendê-la com uma classe, apenas implementá-la. Eu não deixei isso claro.
Fenton

Eu apoiaria a opinião de @Nikos. A herança de interfaces não anula o propósito de melhorar a legibilidade, diminuindo a capacidade de manutenção? Existe alguém que poderia fornecer uma explicação racional de por que usar herança de interfaces em vez de uma nova?
Tomas Hesse

@TomasHesse não existe uma única resposta correta para essa (primeira) pergunta. Essas decisões são trade-offs entre demandas concorrentes e a resposta correta é avaliar o trade-off no contexto apropriado.
Fenton

100

Siga a resposta de @Duncan's @ Bartvds, aqui para fornecer uma maneira viável depois que anos se passaram.

Neste ponto, após o lançamento do Typescript 1.5 (@Jun 15 '15), sua interface útil

interface MyType {
    instanceMethod();
}

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

pode ser implementado desta forma com a ajuda do decorador.

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

Consulte meu comentário no github open issue 13462 .

resultado visual: erro de compilação com uma dica de método estático ausente. insira a descrição da imagem aqui

Após a implementação do método estático, falta a dica para o método. insira a descrição da imagem aqui

A compilação passou depois que a interface estática e a interface normal foram cumpridas. insira a descrição da imagem aqui


5
Sua resposta é A resposta. Muito obrigado. Trabalhando com o
texto datilografado

Esta resposta não está funcionando para mim no Typescript 3.0.1
mmmeff

2
@mmmeff eu testei com 3.0.1 e funciona. O que quer dizer "não funciona"? Existe alguma mensagem de erro?
Val

1
@Chklang Obrigado! Eu atualizei minha resposta com sua sugestão
Val

1
Como podemos fazer isso com uma classe Abstract?
geoyws 01 de

16

Você pode definir a interface normalmente:

interface MyInterface {
    Name:string;
}

mas você não pode simplesmente fazer

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

Para expressar que uma classe deve seguir esta interface para suas propriedades estáticas, você precisa de alguns truques:

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

Você pode até manter o nome da classe, o TypeScript (2.0) não se importará:

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

Se você deseja herdar de muitas interfaces estaticamente, você terá que fundi-las primeiro em uma nova:

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
}

Ou, se não quiser nomear a interface mesclada, você pode fazer:

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
}

Exemplo de trabalho


Recebo o erro TS2322: A classe anônima não pode ser atribuída ao tipo
JoshuaDavid

@FireCoding Qual versão do TypeScript você está usando? Meus exemplos funcionaram com 2.0
Kamil Szot

Esta é uma solução mais direta do que o decorador personalizado @staticImplements.
Robert Penner

1
Eu acho que essa é uma ótima solução. para métodos estáticos assíncronos, você também pode usarreply(msg: IBotMsg): Promise<IBotReply>
dcsan

existe uma maneira de definir os métodos estáticos e de instância ou todos os métodos agora são apenas estáticos?
DCSAN

15

As propriedades estáticas geralmente são colocadas no construtor (global) do objeto, enquanto a palavra-chave "interface" se aplica a instâncias do objeto.

A resposta anterior fornecida é obviamente correta se você estiver escrevendo a classe em TypeScript. Pode ser útil saber que se você está descrevendo um objeto que já está implementado em outro lugar, o construtor global incluindo propriedades estáticas pode ser declarado assim:

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

editar: postado como resposta completa abaixo
Bartvds

Neste ponto, após o lançamento do Typescript 1.5 (05/06/2015), é uma maneira viável de descrever membros estáticos de uma classe. stackoverflow.com/a/43674389/681830
Val

8

Os modificadores estáticos não podem aparecer em um membro de tipo (erro do TypeScript TS1070). É por isso que recomendo usar uma classe abstrata para resolver a missão:

Exemplo

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

Sim, essa é a resposta!
vintproykt

7

A solução de @duncan acima especificando new()para o tipo estático também funciona com interfaces:

interface MyType {
    instanceMethod();
}

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

Qual desses seria minha classe implement?
Denis Pshenov

2
Neste ponto, você não pode usar uma interface para descrever membros estáticos, apenas membros de instância. Portanto, neste exemplo, sua classe implementaria MyType(como em class Foo implements MyType). A interface estática só é realmente útil em definições, ao descrever o código JS existente.
Bartvds

Neste ponto, após o lançamento do Typescript 1.5 (05/06/2015), é uma maneira viável de descrever membros estáticos de uma classe. stackoverflow.com/a/43674389/681830
Val

4

Se você estiver procurando definir uma classe estática (ou seja, todos os métodos / propriedades são estáticos), você pode fazer algo assim:

interface MyStaticClassInterface {
  foo():string;
}

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

Neste caso, a "classe" estática é realmente apenas um objeto-js simples, que implementa todos os métodos de MyStaticClassInterface


4

Sim, é possível. Aqui está a solução

export interface Foo {

    test(): void;
}

export namespace Foo {

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

}

Isso é incrível! Exatamente o que eu precisava
Umar Farooq Khawaja

1
@UmarFarooqKhawaja Fico feliz em ajudar!
Pavel_K

então isso é usar um namespace Foopara substituir uma definição de classe? onde a classe Foo implementaria o statMethod () isso parece um pouco um hack, especialmente porque os namespaces estão meio que obsoletos agora ...
dcsan

@dcsan Sim, é meio hack. Mas o que fazer? Muitas edições datilografadas estão em aberto por muitos anos com o rótulo waiting more feedback. Portanto, você pode esperar até que métodos estáticos sejam adicionados à interface ou fazer desta forma.
Pavel_K

mas também não entendi ... export namespaceé realmente a implementação de sua classe? Isso parece estar distorcendo muito as coisas. Eu me pergunto o que mais isso quebra nas aulas?
DCSAN

3

Outra opção não mencionada aqui é definir a variável com um tipo que representa a interface estática e atribuir a ela uma expressão de classe:

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

O efeito é o mesmo que na resposta com decoradores , mas sem sobrecarga de decoradores

Parque infantil

Sugestão / discussão relevante no GitHub


2

Você pode mesclar interface com namespace usando o mesmo nome:

interface myInterface { }

namespace myInterface {
  Name:string;
}

Mas essa interface só é útil para saber se ela possui propriedades Name. Você não pode implementá-lo.


2

Eu encontrei uma maneira de fazer isso (sem decoradores) para meu caso de uso específico.

A parte importante que verifica se há membros estáticos é IObjectClasse usando cls: IObjectClass<T>no createObjectmétodo:

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

Embora a palavra-chave estática não seja suportada na interface em Typescript, podemos alcançá-la criando um function interface que tenha um membro estático.

Em meu código a seguir, criei uma interface de função Factoryque tem dois membros estáticos serialNumbere 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

Eu implementei uma solução como a de Kamil Szot e teve um efeito indesejado. Eu não tenho reputação suficiente para postar isso como um comentário, então eu posto aqui caso alguém esteja tentando essa solução e leia isso.

A solução é:

interface MyInterface {
    Name: string;
}

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

No entanto, usar uma expressão de classe não me permite usar MyClasscomo um tipo. Se eu escrever algo como:

const myInstance: MyClass;

myInstanceacabou sendo do tipo any, e meu editor mostra o seguinte erro:

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

Acabo perdendo uma digitação mais importante do que a que eu queria conseguir com a interface para a parte estática da aula.

A solução de Val usando um decorador evita essa armadilha.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.