我只想在打字稿界面中声明一个静态属性?我还没有找到任何关于这个的地方。
interface myInterface {
static Name:string;
}
可能吗?
遵循@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 issue 13462 上的评论。
https://i.stack.imgur.com/xV9F7.png
https://i.stack.imgur.com/jummd.png
https://i.stack.imgur.com/awoDo.png
你不能在 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);
extends
关键字在 TS 中扩展接口。您只是不能使用类扩展接口(您需要这样做才能添加静态属性)。
静态修饰符不能出现在类型成员上(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());
您可以正常定义接口:
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
}
工作example
@staticImplements
更直接的解决方案。
reply(msg: IBotMsg): Promise<IBotReply>
静态属性通常放置在对象的(全局)构造函数中,而“interface”关键字适用于对象的实例。
如果您使用 TypeScript 编写课程,那么前面给出的答案当然是正确的。它可能有助于其他人知道,如果您正在描述一个已经在其他地方实现的对象,那么可以像这样声明包含静态属性的全局构造函数:
declare var myInterface : {
new(): Interface;
Name:string;
}
@duncan 为静态类型指定 new()
的解决方案也适用于接口:
interface MyType {
instanceMethod();
}
interface MyTypeStatic {
new():MyType;
staticMethod();
}
implement
?
MyType
(如 class Foo implements MyType
)。在描述现有的 JS 代码时,静态接口仅在定义中真正有用。
此处未提及的另一个选项是使用表示静态接口的类型定义变量并为其分配类表达式:
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() { }
}
效果与 answer with decorators 相同,但没有装饰器的开销
GitHub 上的相关 suggestion/discussion
implements static X, static Y
是个好建议。 static
作为每个界面之前的关键字,虽然有点难看,但不应该破坏任何东西。
解决方案
返回 I
的实例类型并确保 C
扩展 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 的回答(谢谢),这个解决方案:
不需要额外的运行时值
类自己的成员信息被保留
允许构造函数约束
测试
原样 - Playground Link:
MyClass.staticMethod(); // OK
MyClass.ownStaticMethod(); // OK
new MyClass().instanceMethod(); // OK
new MyClass().ownInstanceMethod(); // OK
如果要从 MyClass
- Playground Link 中删除 staticMethod
:
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'.
如果要从 MyClass
- Playground Link 中删除 instanceMethod
:
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'.
StaticImplements
的返回类型应该是 InstanceType<C>
(这也修复了一个错误:“'C' 已声明但它的值从未被读取。”)
C extends I
中,所以返回类型也可能是 object
或 {}
。感觉类更适合实际实现目标实例类型,而不是自身或空对象。据我了解,除非设置了 noUnusedParameters,否则它应该只发出警告。在我的例子中,它是 ESLint 突出显示它,禁用它为该行仅在悬停时显示 TS 警告,否则它会变灰。
我对顶级答案的复杂程度感到有些惊讶!但也许这只是因为这个线程太老了。
编辑:实际上,经过一些测试,我最初的尝试被证明实际上是无用的,而且这个问题比我最初预期的更难解决。
然而,经过大约一个小时左右的修补,我想我可能刚刚找到了迄今为止最好/最干净的解决方案(基于我最初的想法)!如果提出的问题是“如何在界面中包含静态属性?”,那么我认为这是一个相当不错的答案。如果您只需要一个接口(编译时键入/要求/约束),这至少比扩展一个类要好。这也没有真正的缺点(好吧,也许是一个小缺点),因为解决方案是 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
在两个方向(到/从)。
如果您要定义一个静态类(即所有方法/属性都是静态的),您可以执行以下操作:
interface MyStaticClassInterface {
foo():string;
}
var myStaticClass:MyStaticClassInterface = {
foo() {
return 'bar';
}
};
在这种情况下,静态“类”实际上只是一个普通的 ol'-js 对象,它实现了 MyStaticClassInterface
的所有方法
您可以merge interface with namespace使用相同的名称:
interface myInterface { }
namespace myInterface {
Name:string;
}
但是这个接口只有在知道它有属性 Name
时才有用。你不能实现它。
对的,这是可能的。这是解决方案
export interface Foo {
test(): void;
}
export namespace Foo {
export function statMethod(): void {
console.log(2);
}
}
Foo
来代替类定义? Class Foo 将实现 statMethod() 这似乎有点像 hack,尤其是因为命名空间现在有点被弃用了......
waiting more feedback
。所以你要么等到静态方法被添加到接口,要么这样做。
export namespace
实际上是您的 Class 实现?这似乎确实使事情发生了很大变化。我想知道课堂上还有什么其他问题?
我为我的特定用例找到了一种方法(没有装饰器)。
检查静态成员的重要部分是 IObjectClass
并在 createObject
方法中使用 cls: IObjectClass<T>
:
//------------------------
// 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);
这是一个相当简单的方法:
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();
请注意,这不允许您像通常那样使类扩展接口,但在许多情况下这已经足够了。
虽然 Typescript 的接口中不支持 static 关键字,但我们可以通过创建一个具有静态成员的 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();
我实现了一个类似 Kamil Szot's 的解决方案,但效果并不理想。我没有足够的声誉将其发布为评论,因此我将其发布在此处,以防有人尝试该解决方案并阅读此内容。
解决方案是:
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)
我最终失去了一个比我想用类的静态部分的接口实现的更重要的类型。
Val's solution 使用装饰器可以避免这个陷阱。
其他解决方案似乎偏离了幸福的道路,我发现我的场景包含在我在下面解释的 Typescript documentation 中:
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);
我的解决方案非常适合我添加其他静态构造函数的用例。我已经对其进行了测试,并且它通过了所有测试。如果有人发现有问题的边缘案例,请告诉我。
我做了一个泛型类型,它接受接口和静态接口。
它适用于具体和抽象类。
我使用条件类型设计了它,以便所有错误都传播到实现接口的类而不是接口本身。
注意:错误传播允许 vscode 快速修复(实现所有方法)。唯一的缺点是您必须自己应用 static 关键字,因为该错误没有可用的快速修复。
界面:
type Class<T = any> = new (...args: any[]) => T;
type AbstractClass<T = any> = abstract new (...args: any[]) => T;
type Interface<C extends Class<InstanceType<C>> | AbstractClass<InstanceType<C>>, SI, I = {}> =
C extends Class<InstanceType<C>>
// ConcreteClass
? InstanceType<C> extends I
? C extends (SI & Class<InstanceType<C>>)
? (InstanceType<C> & I)
: (SI & Class<InstanceType<C>>) // Indicate StaticInterface Error
: I // Indicate Interface Error
// AbstractClass
: InstanceType<C> extends I
? C extends (SI & AbstractClass<InstanceType<C>>)
? (InstanceType<C> & I)
: (SI & AbstractClass<InstanceType<C>>) // Indicate StaticInterface Error
: I // Indicate Interface Error
用法:
interface MyInterface {
instanceMethod(): number;
}
interface MyStaticInterface {
staticMethod(): number;
}
class MyClass implements Interface<typeof MyClass, MyStaticInterface, MyInterface> {
static staticMethod(): number {
return 50;
}
instanceMethod(): number {
return 100;
}
static otherStatic() {
return "HELLO"
}
otherInstance() {
return "GOODBYE"
}
}
abstract class MyClass1 implements Interface<typeof MyClass1, MyStaticInterface, MyInterface> {
static staticMethod(): number {
return 50;
}
instanceMethod(): number {
return 20;
}
static otherStatic() {
return "HELLO"
}
otherInstance() {
return "GOODBYE"
}
abstract abstractMethod() : number;
}
简单的例子
interface Person {
name: string;
age: number;
}
abstract class Trackable {
static TrackInstances: number;
}
class Pablo extends Trackable implements Person {
constructor(public name: string, public age: number) { Pablo.TrackInstances+=1; }
}
console.log(Pablo.TrackInstances);
'constructor' is declared but its value is never read.ts(6133)
return (constructor: T) => {}