我对顶级答案的复杂程度感到有些惊讶!但也许这只是因为这个帖子太老了。
编辑:实际上,经过一些测试,我最初的尝试被证明是毫无用处的,而且这个问题比我最初预期的要难解决。
然而,经过大约一个小时左右的修补,我想我可能刚刚找到了迄今为止最好/最干净的解决方案(基于我最初的想法)!如果提出的问题是“如何在界面中包含静态属性?”,那么我认为这是一个相当不错的答案。如果您只需要一个接口(编译时键入/要求/约束),这至少比扩展一个类要好。这没有真正的缺点(好吧,也许是一个小缺点),因为解决方案是 100% 环境的(不像某些答案所暗示的基于 extends 的类扩展),并且类是常量(不可变引用,当使用标准的类声明语法而不是我在这里所做的类表达式)。这不会产生运行时开销,也不需要运行时类继承。您可以在一个接口中定义整个类(静态和非静态成员)!
这是怎么做的!
/** ./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 可能是一个不错的主意,这样它的正确使用就很清楚了(编辑:我已经通过将接口封装在一个文件模块,从而使其对所有其他代码都是私有的,然后只导出对它的类型引用)。
这就是它的全部内容!
将接口放入模块并不是完全必要的,但它提供了一些小好处,包括:
-
在整个实现代码中使用的“公开”广泛使用的类型注释变得稍微小一些,因为它不再包含关键字typeof
-
与包装的环境类/接口声明不同,导出的/面向外部的标识符严格来说是一种类型(别名),因此如果有人尝试实例化它或在扩展子句中使用它(或使用它),现在将发生错误任何其他预期运行时值)
在此示例中,我没有为类表达式提供类名,因为类表达式与所有函数表达式一样,如果未提供类名,则只会继承分配给它们的标识符。因此,如果您的标识符与您想要的该类或函数的名称相同,则可以不使用它。或者,您可以像往常一样提供一个内联,它将优先于标识符。类或函数名称也可以在函数/类创建后更改,但只能通过 Object.defineProperty 或 Object.defineProperties。
FWIW 类实际上可以是另一个类的implemented(至少在最新版本的 TS 中),但静态属性无论如何都会被忽略。似乎implementing 任何东西都只适用于prototype 在两个方向(到/从)。