Cómo definir una propiedad estática en la interfaz TypeScript


122

¿Solo quiero declarar una propiedad estática en la interfaz mecanografiada ? No he encontrado nada al respecto.

interface myInterface {
  static Name:string;
}

¿Es posible?


¿Qué estás intentando modelar exactamente?
— Ryan Cavanaugh

5
Considere usar 'clase Foo implementa Bar' donde Bar también es una clase. Puede 'implementar' clases por otras clases en TS.
— Tobias Cudnik

Respuestas:


46

No puede definir una propiedad estática en una interfaz en TypeScript.

Supongamos que desea cambiar el Dateobjeto, en lugar de intentar agregar a las definiciones de Date, puede envolverlo o simplemente crear su clase de fecha enriquecida para hacer las cosas que Dateno funcionan.

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

Debido a que Date es una interfaz en TypeScript, no puedes extenderla con una clase usando la extendspalabra clave, lo cual es un poco vergonzoso, ya que sería una buena solución si date fuera una clase.

Si desea extender el objeto Date para proporcionar una MinValuepropiedad en el prototipo, puede:

interface Date {
    MinValue: Date;
}

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

Llamado usando:

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

Y si quieres que esté disponible sin una instancia, también puedes ... pero es un poco complicado.

interface DateStatic extends Date {
    MinValue: Date;
}

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

Llamado usando:

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

1
@Rajagopal Para ser claros, en realidad puedes extender interfaces en TS usando la extendspalabra clave. Simplemente no puede extender una interfaz con una clase (lo que tendría que hacer para agregar una propiedad estática).
— Jude Fisher

Steve: 'Debido a que Date es una interfaz en TypeScript, no puede extenderla usando la palabra clave extiende', eso no es correcto, ¿verdad?
— Jude Fisher

1
Puede extender la interfaz, pero no puede extenderla con una clase, solo implementarla. No lo dejé claro.
— Fenton

Segunda opinión de @Nikos. ¿La herencia de interfaces no anula el propósito de mejorar la legibilidad y, por lo tanto, disminuir la mantenibilidad? ¿Hay alguien que pueda dar una explicación racional de por qué usar la herencia de interfaces en lugar de una nueva?
— Tomas Hesse

@TomasHesse no hay una sola respuesta correcta a esa (primera) pregunta. Estas decisiones son compensaciones entre demandas en competencia y la respuesta correcta es evaluar la compensación en el contexto apropiado.
— Fenton

100

Siga la respuesta de @Bartvds de @ Duncan, aquí para proporcionar una forma viable después de que pasaron los años.

En este punto, después del lanzamiento de Typescript 1.5 (@Jun 15 '15), su interfaz útil

interface MyType {
    instanceMethod();
}

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

se puede implementar de esta manera con la ayuda del 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 mi comentario en github open issue 13462 .

resultado visual: error de compilación con un indicio de falta de método estático. ingrese la descripción de la imagen aquí

Después de implementar el método estático, se indica que falta el método. ingrese la descripción de la imagen aquí

La compilación pasó después de que se cumplieron tanto la interfaz estática como la interfaz normal. ingrese la descripción de la imagen aquí


5
Tu respuesta es LA respuesta. Muchas gracias. Trabajando con TypeScript 2.4.1
— Juan Camilo Guarin P

Esta respuesta no me funciona en Typecript 3.0.1
— mmmeff

2
@mmmeff Probé con 3.0.1 y funciona. ¿Qué quieres decir con "no funciona"? ¿Hay algún mensaje de error?
— Val

1
@Chklang ¡Gracias! Actualicé mi respuesta con su sugerencia
— Val

1
¿Cómo podemos hacer esto con una clase abstracta?
— geoyws

16

Puede definir la interfaz normalmente:

interface MyInterface {
    Name:string;
}

pero no puedes simplemente hacer

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

Para expresar que una clase debe seguir esta interfaz por sus propiedades estáticas, necesita un poco de engaño:

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

Incluso puede mantener el nombre de la clase, a TypeScript (2.0) no le importará:

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

Si desea heredar de muchas interfaces de forma estática, primero deberá fusionarlas en una nueva:

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
}

O si no desea nombrar la interfaz fusionada, puede hacer:

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
}

Ejemplo de trabajo


Recibo el error TS2322: la clase anónima no se puede
— asignar

@FireCoding ¿Qué versión de TypeScript estás usando? Mis ejemplos funcionaron con 2.0
— Kamil Szot

Esta es una solución más sencilla que el decorador personalizado @staticImplements.
— Robert Penner

1
Creo que esta es una gran solución. para métodos estáticos asíncronos también puede usarreply(msg: IBotMsg): Promise<IBotReply>
— dcsan

¿Hay alguna manera de definir métodos estáticos y de instancia o todos los métodos ahora son solo estáticos?
— dcsan

15

Las propiedades estáticas generalmente se colocan en el constructor (global) del objeto, mientras que la palabra clave "interfaz" se aplica a las instancias del objeto.

La respuesta anterior dada es, por supuesto, correcta si está escribiendo la clase en TypeScript. Puede ayudar a otros saber que si está describiendo un objeto que ya está implementado en otro lugar, entonces el constructor global, incluidas las propiedades estáticas, se puede declarar así:

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

editar: publicado como una respuesta completa a continuación
— Bartvds

En este punto, después del lanzamiento de Typescript 1.5 (6.15.2015), aquí hay una forma viable de describir los miembros estáticos de una clase. stackoverflow.com/a/43674389/681830
— Val

8

Los modificadores estáticos no pueden aparecer en un miembro de tipo (error de TypeScript TS1070). Por eso recomiendo usar una clase abstracta para resolver la misión:

Ejemplo

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

¡Sí, esa es la respuesta!
— vintproykt

7

La solución de @ duncan anterior que especifica new()el tipo estático también funciona con interfaces:

interface MyType {
    instanceMethod();
}

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

¿Cuál de estos sería mi clase implement?
— Denis Pshenov

2
En este punto, no puede usar una interfaz para describir miembros estáticos, solo miembros de instancia. Entonces, en este ejemplo, su clase implementaría MyType(como en class Foo implements MyType). La interfaz estática solo es realmente útil en las definiciones, cuando se describe el código JS existente.
— Bartvds

En este punto, después del lanzamiento de Typescript 1.5 (6.15.2015), aquí hay una forma viable de describir los miembros estáticos de una clase. stackoverflow.com/a/43674389/681830
— Val

4

Si está buscando definir una clase estática (es decir, todos los métodos / propiedades son estáticos), puede hacer algo como esto:

interface MyStaticClassInterface {
  foo():string;
}

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

En este caso, la "clase" estática es en realidad un objeto simple-ol'-js, que implementa todos los métodos de MyStaticClassInterface


4

Sí, es posible. Aqui esta la solucion

export interface Foo {

    test(): void;
}

export namespace Foo {

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

}

¡Esto es asombroso! Exactamente lo que necesitaba
— Umar Farooq Khawaja

1
@UmarFarooqKhawaja ¡Me alegra ayudar!
— Pavel_K

¿Entonces esto está usando un espacio Foode nombres para reemplazar una definición de clase? donde Class Foo implementaría statMethod (), esto parece un truco, especialmente porque los espacios de nombres están en desuso ahora ...
— dcsan

@dcsan Sí, es un truco. ¿Pero qué hacer? Muchos números de mecanografiado están abiertos durante muchos años con etiqueta waiting more feedback. Por lo tanto, espere hasta que se agreguen métodos estáticos a la interfaz o hágalo de esta manera.
— Pavel_K

pero tampoco lo entiendo del todo ... ¿cuál export namespacees realmente la implementación de tu clase? Eso parece estar doblando mucho las cosas. Me pregunto qué más se rompe en las clases.
— dcsan

3

Otra opción que no se menciona aquí es definir la variable con un tipo que representa la interfaz estática y asignarle una expresión de clase:

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

El efecto es el mismo que en la respuesta con decoradores , pero sin gastos generales de decoradores.

Patio de recreo

Sugerencia / discusión relevante en GitHub



2

Encontré una manera de hacer esto (sin decoradores) para mi caso de uso específico.

La parte importante que verifica los miembros estáticos es IObjectClassy usar cls: IObjectClass<T>en el 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

Aunque la palabra clave estática no es compatible con la interfaz de TypeScript, podemos lograrlo creando un function interfacemiembro que tenga estático.

En mi siguiente código, he creado una interfaz de función Factoryque tiene dos miembros estáticos serialNumbery 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

Implementé una solución como la de Kamil Szot y tiene un efecto no deseado. No tengo suficiente reputación para publicar esto como comentario, así que lo publico aquí en caso de que alguien intente esa solución y lea esto.

La solucion es:

interface MyInterface {
    Name: string;
}

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

Sin embargo, usar una expresión de clase no me permite usar MyClasscomo tipo. Si escribo algo como:

const myInstance: MyClass;

myInstanceresulta ser de tipo any, y mi editor muestra el siguiente error:

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

Termino perdiendo una escritura más importante que la que quería lograr con la interfaz para la parte estática de la clase.

La solución de Val usando un decorador evita este error.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.