180

我只想在打字稿界面中声明一个静态属性?我还没有找到任何关于这个的地方。

interface myInterface {
  static Name:string;
}

是否可以?

4

17 回答 17

163

遵循@Duncan 的@Bartvds 的回答,在这里提供多年过去后的可行方法。

在 Typescript 1.5 发布后(@Jun 15 '15),您的有用界面

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问题 13462上的评论。

视觉结果:编译错误,提示缺少静态方法。 在此处输入图像描述

静态方法实现后,提示缺少方法。 在此处输入图像描述

静态接口和普通接口都完成后编译通过。 在此处输入图像描述

于 2017-04-28T07:24:17.387 回答
64

你不能在 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);
于 2012-12-19T15:25:46.847 回答
19

静态修饰符不能出现在类型成员上(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());
于 2019-08-01T09:48:11.393 回答
18

您可以正常定义接口:

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
}

工作示例

于 2016-10-12T18:45:24.940 回答
16

静态属性通常放置在对象的(全局)构造函数中,而“interface”关键字适用于对象的实例。

如果您使用 TypeScript 编写课程,那么前面给出的答案当然是正确的。它可能有助于其他人知道,如果您正在描述一个已经在其他地方实现的对象,那么可以像这样声明包含静态属性的全局构造函数:

declare var myInterface : {
  new(): Interface;
  Name:string;
}
于 2012-12-20T14:54:16.267 回答
7

new()上面指定静态类型的@duncan 解决方案也适用于接口:

interface MyType {
    instanceMethod();
}

interface MyTypeStatic {
    new():MyType;
    staticMethod();
}
于 2013-09-16T21:00:55.457 回答
7

此处未提及的另一个选项是使用表示静态接口的类型定义变量并为其分配类表达式:

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上的相关建议/讨论

于 2019-12-02T06:58:36.907 回答
4

如果您要定义一个静态类(即所有方法/属性都是静态的),您可以执行以下操作:

interface MyStaticClassInterface {
  foo():string;
}

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

在这种情况下,静态“类”实际上只是一个普通的-ol'-js-object,它实现了所有的方法MyStaticClassInterface

于 2015-01-11T20:49:38.103 回答
4

是的,有可能。这是解决方案

export interface Foo {

    test(): void;
}

export namespace Foo {

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

}
于 2019-04-22T07:22:28.907 回答
4

我对顶级答案的复杂程度感到有些惊讶!但也许这只是因为这个线程太老了。

编辑:实际上,经过一些测试,我最初的尝试被证明实际上是无用的,而且这个问题比我最初预期的更难解决。

然而,经过大约一个小时左右的修补,我想我可能刚刚找到了迄今为​​止最好/最干净的解决方案(基于我最初的想法)!如果提出的问题是“如何在界面中包含静态属性?” ,那么我认为这是一个相当不错的答案。如果您只需要一个接口(编译时键入/要求/约束​​),这至少比扩展一个类要好。由于解决方案是 100% 环境(不像extends-based 类扩展,就像一些答案所暗示的那样)和类都是常量(不可变引用,在使用标准类声明语法而不是像我在这里所做的那样使用类表达式时不会提升)。这不会产生运行时开销,也不需要运行时类继承。您可以在一个接口中定义整个类(静态和非静态成员)(环境类用作)!

这是它是如何完成的!

/** ./interface.ts */
// In a file module (best), or wrap in ts module or namespace block

// Putting this in a module provides encapsulation ensuring that no one is
// at risk of misusing this class (it must be used as a type only). 

// Export only a type reference which will make it error is someone tries 
// to use it as a value (such as in an `extends` clause, or trying to 
// instantiate it).

/** 
 * Other Ideas For Names To Differentiate From Actual Classes/Non-Ambient Values:
 * MyClassInterface or _Interface_MyClass or MyClass_Interface or Interface_MyClass  
 **/
declare class _MyClassInterface {
    static staticProp: string;
    static staticMethod(): number;
    readonly prop: boolean 
    /** 
     * Note: Above, readonly won't need to be specified in the real class 
     * but the prop *will* still be readonly anyway.
     *
     * Now for the only caveat!
     * It does appear however that you cannot mark anything private or 
     * protected in this pseudo-interface which is a bummer, only props
     * and methods that appear only in the real class can be.
     */
    prop2: boolean;
    method(): Function;
    constructor(p1: string, p2: number);
}

export type MyClassInterface = typeof _MyClassInterface;

现在使用接口

/** ./consumer.ts */
import { MyClassInterface } from "./interface" // type import

const MyClass: MyClassInterface = class {
    static staticProp: string;
    prop: boolean;
    prop2: boolean;
    protected onlyOnRealClass: boolean; /* this is ok since this prop doesn't exist on the interface */

    static staticMethod() {
        return 5;
    }

    method() {
        return () => {};
    }

    constructor(p1: string, p2: number) {}
};

请注意,这里的typeof关键字是绝对必要的(如果我没记错的话,那是因为没有它,打字稿认为我们正在指定实例类型,而我们真正想要的是类本身的类型)。例如当我们做

const a: MyClass = new MyClass()

没有typeof关键字,我们说它a应该是 MyClass 的一个实例而不是 MyClass 本身。

abstract确保您不会意外尝试实例化该类...

编辑:实际上我从我的答案中删除了 abstract 关键字,因为事实证明,真正的类实际上继承了环境类的抽象属性(有意义),因此如果编译器抱怨,则不会实例化提供其类型的环境类被标记为抽象...如果环境类被意外实例化,则只需要处理 ts 不会出错。然后在环境类声明/名称前加上下划线和/或Interface在名称中包含该单词可能是一个不错的主意,这样它的正确使用就很清楚了(编辑:我已经通过将接口封装在文件模块中来解决这个问题从而使其对所有其他代码私有,然后仅导出对它的类型引用)。

这就是它的全部内容!

将接口放入模块并不是完全必要的,但它提供了一些小好处,包括:

  1. 在整个实现代码中使用的“公开”广泛使用的类型注释变得稍微小一些,因为它不再包含关键字typeof

  2. 与包装的环境类/接口声明不同,导出的/面向外部的标识符严格来说是一种类型(别名),因此如果有人尝试实例化它或在扩展子句中使用它(或在其他任何地方使用它),则会发生错误预计运行时值)

在此示例中,我没有为类表达式提供类名,因为类表达式与所有函数表达式一样,如果未提供类名,则只会继承分配给它们的标识符。因此,如果您的标识符与您想要的该类或函数的名称相同,则可以不使用它。或者,您可以像往常一样提供一个内联,它将优先于标识符。类或函数名也可以在函数/类创建后更改,但只能通过 Object.defineProperty 或 Object.defineProperties。

FWIW 类实际上可以implemented(至少在最新版本的 TS 中)由另一个类,但静态属性无论如何都会被忽略。似乎implementing任何事情都只适用prototype于两个方向(到/从)。

于 2021-04-05T01:27:56.533 回答
4

解决方案

返回实例类型I并确保Cextends I

type StaticImplements<I extends new (...args: any[]) => any, C extends I> = InstanceType<I>;

与实例方法的接口:

interface MyInstance {
    instanceMethod();
}

与静态方法的接口:

interface MyClassStatic {
    new (...args: any[]): MyInstance;
    staticMethod();
}

需要静态方法并使用自己的方法扩展的类:

class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {
    static staticMethod();
    static ownStaticMethod();
    instanceMethod();
    ownInstanceMethod();
}

推理

在接口中定义静态方法在#33892中讨论,抽象静态方法在#34516中讨论。

根据 Val 和 Aleksey 的回答(谢谢),这个解决方案:

  • 不需要额外的运行时值
  • 类自己的成员信息被保留
  • 允许构造函数约束

测试

原样 -游乐场链接

MyClass.staticMethod(); // OK
MyClass.ownStaticMethod(); // OK
new MyClass().instanceMethod(); // OK
new MyClass().ownInstanceMethod(); // OK

如果staticMethod要从MyClass- Playground Link中删除:

class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {} // Type 'typeof MyClass' does not satisfy the constraint 'MyClassStatic'. Property 'staticMethod' is missing in type 'typeof MyClass' but required in type 'MyClassStatic'.

如果instanceMethod要从MyClass- Playground Link中删除:

class MyClass implements StaticImplements<MyClassStatic, typeof MyClass> {} // Class 'MyClass' incorrectly implements interface 'MyInstance'. Property 'instanceMethod' is missing in type 'MyClass' but required in type 'MyInstance'.
于 2021-10-14T13:09:37.743 回答
3

您可以使用相同的名称将接口与命名空间合并:

interface myInterface { }

namespace myInterface {
  Name:string;
}

但是这个接口只有知道它有属性才有用Name。你不能实现它。

于 2017-02-17T12:01:07.953 回答
2

我为我的特定用例找到了一种方法(没有装饰器)。

检查静态成员的重要部分是IObjectClasscls: 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);
于 2017-06-15T18:01:57.163 回答
1

虽然 Typescript 的接口中不支持 static 关键字,但我们可以通过创建一个function interface具有静态成员的来实现它。

在我的以下代码中,我创建了一个Factory具有两个静态成员serialNumberprintSerial的函数接口。

// 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();
于 2019-11-03T08:00:03.603 回答
1

这是一个相当简单的方法:

interface MyClass {
    new (): MyClassInstance;
    staticMethod(): string;
}

interface MyClassInstance {
    instanceMethod(): string;
}

const Class: MyClass = class {
    static staticMethod() {
        return "This is a static method";
    }
    instanceMethod() {
        return "This is an instance method";
    }
}

Class.staticMethod();

// Has type MyClassInstance
const instance = new Class();
instance.instanceMethod();

请注意,这不允许您像通常那样使类扩展接口,但在许多情况下这已经足够了。

于 2021-09-08T03:28:45.453 回答
0

我实施了类似Kamil Szot's的解决方案,但效果不佳。我没有足够的声誉将其发布为评论,因此我将其发布在此处,以防有人尝试该解决方案并阅读此内容。

解决方案是:

interface MyInterface {
    Name: string;
}

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

但是,使用类表达式不允许我MyClass用作类型。如果我写类似:

const myInstance: MyClass;

myInstance结果是 type any,我的编辑器显示以下错误:

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

我最终失去了一个比我想用类的静态部分的接口实现的更重要的类型。

Val使用装饰器的解决方案避免了这个陷阱。

于 2020-07-14T22:06:49.587 回答
0

其他解决方案似乎偏离了幸福的道路,我发现我的场景包含在我在下面解释的Typescript 文档中:

interface AppPackageCheck<T> {
  new (packageExists: boolean): T
  checkIfPackageExists(): boolean;
}

class WebApp {
    public static checkIfPackageExists(): boolean {
        return false;
    }

    constructor(public packageExists: boolean) {}
}

class BackendApp {
    constructor(public packageExists: boolean) {}
}

function createApp<T>(type: AppPackageCheck<T>): T {
    const packageExists = type.checkIfPackageExists();
    return new type(packageExists)
}

let web = createApp(WebApp);

// compiler failure here, missing checkIfPackageExists
let backend = createApp(BackendApp); 
于 2020-10-02T14:10:56.040 回答