TypeScript 静态类
Posted
技术标签:
【中文标题】TypeScript 静态类【英文标题】:TypeScript static classes 【发布时间】:2012-10-24 03:11:05 【问题描述】:我想从传统的 JS 迁移到 TypeScript,因为我喜欢类似 C# 的语法。 我的问题是我不知道如何在 TypeScript 中声明静态类。
在 C# 中,我经常使用静态类来组织变量和方法,将它们放在一个命名类中,而无需实例化对象。 在 vanilla JS 中,我曾经用一个简单的 JS 对象来做到这一点:
var myStaticClass =
property: 10,
method: function()
在 TypeScript 中,我宁愿采用我的 C-sharpy 方法,但似乎 TS 中不存在静态类。 这个问题的合适解决方案是什么?
【问题讨论】:
我想指出,即使使用像您的 JS 示例中那样的文字对象,也是类型安全的并且可以实现您想要的。 【参考方案1】:自 TypeScript 1.6 以来,抽象类一直是 TypeScript 的一等公民。你不能实例化一个抽象类。
这是一个例子:
export abstract class MyClass
public static myProp = "Hello";
public static doSomething(): string
return "World";
const okay = MyClass.doSomething();
//const errors = new MyClass(); // Error
【讨论】:
在处理静态类时,这是最好的回应,我对此表示赞同。Singleton
用于同一类实例的共享内存模式。此外,静态类根据定义没有实例,因此如果客户端尝试对其进行初始化,则必须抛出异常。
我们可以做一个抽象类吗?
@KimchiMan - 是的,TypeScript 支持 abstract
关键字。
更新为使用abstract
! @KimchiMan - 好主意!
这是不是比将构造函数标记为私有更好的方法?【参考方案2】:
TypeScript 不是 C#,因此您不必期望 TypeScript 中的 C# 概念相同。问题是你为什么想要静态类?
在 C# 中,静态类只是一个不能被子类化并且必须只包含静态方法的类。 C# 不允许在类之外定义函数。然而,在 TypeScript 中这是可能的。
如果您正在寻找将函数/方法放入命名空间(即非全局)的方法,您可以考虑使用 TypeScript 的模块,例如
module M
var s = "hello";
export function f()
return s;
这样你可以在外部访问m.f(),但不能访问s,并且不能扩展模块。
有关更多详细信息,请参阅 TypeScript specification。
【讨论】:
所以一个模块可以有一个静态方法,但一个类不能?但模块不能有静态数据。它不像 JS 那样方便地包装数据和代码而无需实例化任何东西。 在html
中包含.js
可能会很有用。因此,对于Angular 2
,您可能正在使用System
...所以在bootstrap import
之前使用System.import("Folder/M");
(或编译后的.js
文件的路径)
已弃用。 tslint 也不会再让你为模块和命名空间这样做了。在这里阅读:palantir.github.io/tslint/rules/no-namespace
@florian leitgeb 那么首选方法是什么,只有静态方法和/或抽象关键字的类?与现在似乎已弃用的模块相比,这似乎很糟糕
有可能,我建议看看***.com/a/13222267/5724101【参考方案3】:
在Typescript Language Specification 的 8.2.1 中描述了定义类的静态属性和方法:
class Point
constructor(public x: number, public y: number)
throw new Error('cannot instantiate using a static class');
public distance(p: Point)
var dx = this.x - p.x;
var dy = this.y - p.y;
return Math.sqrt(dx * dx + dy * dy);
static origin = new Point(0, 0);
static distance(p1: Point, p2: Point)
return p1.distance(p2);
其中Point.distance()
是一个静态(或“类”)方法。
【讨论】:
这显示了如何创建一个静态方法,它没有回答关于静态类的问题(除非真正的问题实际上是关于静态方法)。 感谢您的评论。它描述了如何创建静态属性和方法,它们结合在一起允许创建一个具有数据和功能的类,而无需实例化。虽然不是专门的“静态类”,但它确实满足了 OP 的 javascript 示例所描述的要求。 c# 在版本 2 之前没有静态类。它们存在于 c# 中只是为了防止您实例化它们。你不能用 javascript 做到这一点,所以它没有多大意义 @Simon_Weaver 你不能用javascript做什么?防止类被实例化?无论如何,Typescript 并不关心运行时,只要您在尝试执行不允许执行的操作时遇到编译错误,这就是我们所需要的。 我想我的意思是'我们在类级别没有静态关键字(这意味着你不能创建类的实例)'直到版本 2。但再次阅读它我认为我的评论无论如何都没有抓住重点。 OP 并没有真正为整个班级寻找“静态关键字”【参考方案4】:这个问题已经过时了,但我想留下一个利用当前语言版本的答案。不幸的是,TypeScript 中仍然不存在静态类,但是您可以使用私有构造函数编写一个行为相似的类,只需少量开销即可防止从外部实例化类。
class MyStaticClass
public static readonly property: number = 42;
public static myMethod(): void /* ... */
private constructor() /* noop */
这个 sn-p 将允许您使用类似于 C# 对应物的“静态”类,唯一的缺点是仍然可以从内部实例化它们。幸运的是,虽然您不能使用私有构造函数扩展类。
【讨论】:
【参考方案5】:我今天(2018 年 7 月 31 日)遇到了同样的用例,发现这是一种解决方法。它基于我的研究并且对我有用。 期望 - 在 TypeScript 中实现以下目标:
var myStaticClass =
property: 10,
method: function()
我这样做了:
//MyStaticMembers.ts
namespace MyStaticMembers
class MyStaticClass
static property: number = 10;
static myMethod() ...
export function Property(): number
return MyStaticClass.property;
export function Method(): void
return MyStaticClass.myMethod();
因此我们将按如下方式使用它:
//app.ts
/// <reference path="MyStaticMembers.ts" />
console.log(MyStaticMembers.Property);
MyStaticMembers.Method();
这对我有用。如果有人有其他更好的建议,请让我们都听到!!! 谢谢...
【讨论】:
【参考方案6】:这是一种方式:
class SomeClass
private static myStaticVariable = "whatever";
private static __static_ctor = (() => /* do static constructor stuff :) */ )();
__static_ctor
这里是一个立即调用的函数表达式。 Typescript 将在生成的类结束时输出调用它的代码。
更新:对于静态构造函数中不再允许被静态成员引用的泛型类型,您现在需要一个额外的步骤:
class SomeClass<T>
static myStaticVariable = "whatever";
private ___static_ctor = (() => var someClass:SomeClass<T> ; /* do static constructor stuff :) */ )();
private static __static_ctor = SomeClass.prototype.___static_ctor();
当然,在任何情况下,你都可以在类之后调用泛型静态构造函数,例如:
class SomeClass<T>
static myStaticVariable = "whatever";
private __static_ctor = (() => var example: SomeClass<T>; /* do static constructor stuff :) */ )();
SomeClass.prototype.__static_ctor();
请记住不要在上面的__static_ctor
中使用this
(显然)。
【讨论】:
这种方式仍然会为类发出一个构造函数。 "This way" 是一种 hack,不会像预期的那样改变编译器的正常操作。甚至class SomeClass
也会生成一个构造函数——几乎不值得评论,好像引入了一个新问题一样。 ;) 仅供参考:JS 中没有真正的“构造函数”——只有在对象上或通过new
调用时具有“this”的函数。这对于任何“类”来说都是存在的。【参考方案7】:
存在 C# 等语言中的静态类,因为没有其他***构造可以对数据和函数进行分组。然而,在 JavaScript 中,它们确实如此,因此像您所做的那样只声明一个对象要自然得多。为了更接近地模仿类语法,您可以像这样声明方法:
const myStaticClass =
property: 10,
method()
【讨论】:
这种方法不会混淆您的工作流程吗?一方面,您使用类和实例,然后突然又开始在常规的旧 JS 对象中声明数据……使用静态类也有助于提高可读性,这不仅仅是语言的内部工作原理。 我不觉得它会影响我的工作流程;相反,我发现在模块中使用无类 JavaScrpit 对象或仅使用普通函数和常量非常方便。不过,我也尽量避免使用全局状态,因此我很少需要静态类变量之类的东西。【参考方案8】:使用 ES6 外部模块可以这样实现:
// privately scoped array
let arr = [];
export let ArrayModule =
add: x => arr.push(x),
print: () => console.log(arr),
这可以防止使用被 TSLint [1] [2] 认为是不好的做法的内部模块和命名空间,允许私有和公共范围,并防止不需要的类对象的初始化。
【讨论】:
【参考方案9】:见http://www.basarat.com/2013/04/typescript-static-constructors-for.html
这是一种“伪造”静态构造函数的方法。这并非没有危险 - 请参阅referenced codeplex item。
class Test
static foo = "orig";
// Non void static function
static stat()
console.log("Do any static construction here");
foo = "static initialized";
// Required to make function non void
return null;
// Static variable assignment
static statrun = Test.stat();
// Static construction will have been done:
console.log(Test.foo);
【讨论】:
【参考方案10】:实现此目的的一种可能方法是在另一个类中拥有一个类的静态实例。例如:
class SystemParams
pageWidth: number = 8270;
pageHeight: number = 11690;
class DocLevelParams
totalPages: number = 0;
class Wrapper
static System: SystemParams = new SystemParams();
static DocLevel: DocLevelParams = new DocLevelParams();
然后可以使用 Wrapper 访问参数,而无需声明它的实例。例如:
Wrapper.System.pageWidth = 1234;
Wrapper.DocLevel.totalPages = 10;
因此,您可以获得 JavaScript 类型对象的好处(如原始问题中所述),但还可以添加 TypeScript 类型。此外,它避免了必须在类中的所有参数前面添加“静态”。
【讨论】:
【参考方案11】:您可以将抽象类与public static readonly members一起使用,以实现与您正在寻找的非常相似的东西。我相信您正在寻找类似 C# 或 C/C++ 的 struct
之类的东西来将小块数据组织在一起。
抽象类最酷的地方在于
它们不能被实例化, 它们只能派生自和 它们可以为其中定义的部分或全部方法提供基本实现。您甚至可以使用这种技术在某种程度上模仿enum
(例如,您不能打开它们),但具有的属性可以不仅仅是字符串或数字。
// you can omit the public keyword because it's the default in TS, I left it here for clarity
export abstract class RequestTypes
public static readonly All = 'All types';
public static readonly Partners = 'Partners';
public static readonly Articles = 'Articles';
【讨论】:
您可以在 JavaScript/TypeScript 中打开任何值(或表达式)。 查看我的答案:***.com/a/69312788/2339176【参考方案12】:我正在寻找类似的东西,偶然发现了一个叫做Singleton Pattern
的东西。
参考:Singleton Pattern
我正在开发一个 BulkLoader 类来加载不同类型的文件和 想为它使用单例模式。这样我可以加载文件 从我的主应用程序类中轻松检索加载的文件 来自其他类。
下面是一个简单的示例,您可以如何为游戏制作得分管理器 使用 TypeScript 和 Singleton 模式。
类 SingletonClass
private static _instance:SingletonClass = new SingletonClass(); private _score:number = 0; constructor() if(SingletonClass._instance) throw new Error("Error: Instantiation failed: Use SingletonDemo.getInstance() instead of new."); SingletonClass._instance = this; public static getInstance():SingletonClass return SingletonClass._instance; public setScore(value:number):void this._score = value; public getScore():number return this._score; public addPoints(value:number):void this._score += value; public removePoints(value:number):void this._score -= value;
然后,您可以在其他班级的任何地方访问 单身人士:
var scoreManager = SingletonClass.getInstance(); scoreManager.setScore(10); scoreManager.addPoints(1); scoreManager.removePoints(2); console.log( scoreManager.getScore() );
【讨论】:
【参考方案13】:您也可以使用关键字namespace
来组织您的变量、类、方法等。见doc
namespace Validation
export interface StringValidator
isAcceptable(s: string): boolean;
const lettersRegexp = /^[A-Za-z]+$/;
const numberRegexp = /^[0-9]+$/;
export class LettersOnlyValidator implements StringValidator
isAcceptable(s: string)
return lettersRegexp.test(s);
export class ZipCodeValidator implements StringValidator
isAcceptable(s: string)
return s.length === 5 && numberRegexp.test(s);
【讨论】:
查看 Florian 上面的评论“这已被弃用。而且 tslint 不会让你再为模块和命名空间这样做。阅读此处:palantir.github.io/tslint/rules/no-namespace” 【参考方案14】:您可以在 Typescript 中创建一个类,如下所示:
export class Coordinate
static x: number;
static y: number;
static gradient()
return y/x;
并在“没有”实例化的情况下引用它的属性和方法:
Coordinate.x = 10;
Coordinate.y = 10;
console.log(`x of $Coordinate.x and y of $Coordinate.y has gradient of $Coordinate.gradient()`);
Fyi 使用反引号 `` 和插值语法 $ 可以轻松地将代码与文本混合 :-)
【讨论】:
或者你可以导出一个abstract class
,这在 TypeScript 中是可行的。添加static readonly
成员,您基本上就拥有了相当于静态C# 类的功能。【参考方案15】:
我的首选方法是只使用一个 const 对象(主要是代替枚举):
const RequestTypes2 =
All: 'All types',
Partners: 'Partners',
Articles: 'Articles',
as const; // need the "const" to force the property types to be string literal types (hover RequestTypes2 to see!)
// now you can do this (hover AllowedRequestTypes to see the inferred type)
type AllowedRequestTypes = typeof RequestTypes2[keyof typeof RequestTypes2];
function doRequest(requestType: AllowedRequestTypes)
// these should work
doRequest('Partners');
doRequest(RequestTypes2.All);
doRequest(RequestTypes.Articles); // the property's type is "Articles" (string literal type)
// this fails
doRequest('Incorrect');
检查this TS playground。
【讨论】:
以上是关于TypeScript 静态类的主要内容,如果未能解决你的问题,请参考以下文章
带有 typescript 和 mongoose 的静态方法会:“一个接口只能扩展一个类或另一个接口。”