182

我想从传统的 JS 迁移到 TypeScript,因为我喜欢类似 C# 的语法。我的问题是我不知道如何在 TypeScript 中声明静态类。

在 C# 中,我经常使用静态类来组织变量和方法,将它们放在一个命名类中,而无需实例化对象。在 vanilla JS 中,我曾经使用一个简单的 JS 对象来执行此操作:

var myStaticClass = {
    property: 10,
    method: function(){}
}

在 TypeScript 中,我宁愿采用我的 C-sharpy 方法,但似乎 TS 中不存在静态类。这个问题的适当解决方案是什么?

4

15 回答 15

244

自 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
于 2012-11-04T19:54:38.953 回答
188

TypeScript 不是 C#,所以你不应该期望 TypeScript 中的 C# 的相同概念。问题是你为什么想要静态类?

在 C# 中,静态类只是一个不能被子类化并且必须只包含静态方法的类。C# 不允许在类之外定义函数。然而,在 TypeScript 中这是可能的。

如果您正在寻找将函数/方法放入命名空间(即非全局)的方法,您可以考虑使用 TypeScript 的模块,例如

module M {
    var s = "hello";
    export function f() {
        return s;
    }
}

这样你就可以在外部访问Mf(),但不能访问s,并且不能扩展模块。

有关更多详细信息,请参阅 TypeScript规范

于 2012-11-03T19:24:49.347 回答
75

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); 
  } 
}

wherePoint.distance()是静态(或“类”)方法。

于 2012-11-03T19:31:26.613 回答
25

这个问题已经过时了,但我想留下一个利用当前语言版本的答案。不幸的是,TypeScript 中仍然不存在静态类,但是您可以使用私有构造函数编写一个行为相似的类,只需少量开销即可防止从外部实例化类。

class MyStaticClass {
    public static readonly property: number = 42;
    public static myMethod(): void { /* ... */ }
    private constructor() { /* noop */ }
}

此代码段将允许您使用类似于 C# 对应项的“静态”类,唯一的缺点是仍然可以从内部实例化它们。幸运的是,尽管您不能使用私有构造函数扩展类。

于 2018-04-25T11:57:38.310 回答
9

这是一种方式:

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

只要记住永远不要this__static_ctor上面使用(显然)。

于 2013-07-18T16:57:21.747 回答
9

我今天(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();

这对我有用。如果有人有其他更好的建议,请让我们都听到!!!谢谢...

于 2018-07-31T09:08:46.170 回答
5

C# 等语言中的静态类之所以存在,是因为没有其他顶级结构可以对数据和函数进行分组。然而,在 JavaScript 中,它们会这样做,因此像您所做的那样只声明一个对象要自然得多。为了更接近地模仿类语法,您可以像这样声明方法:

const myStaticClass = {
    property: 10,

    method() {

    }
}
于 2017-03-31T05:21:39.210 回答
2

使用 ES6 外部模块可以这样实现:

// privately scoped array
let arr = [];

export let ArrayModule = {
    add: x => arr.push(x),
    print: () => console.log(arr),
}

这可以防止使用被 TSLint [1] [2]认为是不好的做法的内部模块和命名空间,允许私有和公共范围,并防止初始化不需要的类对象。

于 2020-01-22T05:59:50.750 回答
1

http://www.basarat.com/2013/04/typescript-static-constructors-for.html

这是一种“伪造”静态构造函数的方法。这并非没有危险 - 请参阅引用的 codeplex 项目

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);
于 2014-05-23T02:03:14.937 回答
1

实现此目的的一种可能方法是在另一个类中拥有一个类的静态实例。例如:

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 类型的好处。此外,它避免了必须在类中的所有参数前面添加“静态”。

于 2017-11-28T17:00:30.493 回答
1

您可以使用带有公共静态只读成员的抽象类来实现与您正在寻找的非常相似的东西。我相信您正在寻找类似来自 C# 或 C/C++ 的东西来将小块数据组织在一起。struct

抽象类最酷的地方在于

  • 它们不能被实例化,
  • 它们只能来自和
  • 它们可以为其中定义的部分或全部方法提供基本实现。

您甚至可以使用此技术在某种程度上模仿 an 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';
}
于 2021-07-05T14:09:47.117 回答
0

我正在寻找类似的东西,遇到了一个叫做Singleton Pattern.

参考:单例模式

我正在开发一个 BulkLoader 类来加载不同类型的文件,并希望对其使用 Singleton 模式。通过这种方式,我可以从我的主应用程序类中加载文件,并轻松地从其他类中检索加载的文件。

下面是一个简单的示例,如何使用 TypeScript 和 Singleton 模式为游戏制作分数管理器。

类单例类 {

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;
}   }

然后在您的其他课程中的任何地方,您都可以通过以下方式访问 Singleton:

var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10); scoreManager.addPoints(1);
scoreManager.removePoints(2); console.log( scoreManager.getScore() );
于 2016-05-09T17:34:24.820 回答
0

您还可以使用关键字namespace来组织您的变量、类、方法等。见文档

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);
        }
    }
}
于 2017-12-23T06:52:24.713 回答
0

您可以在 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 使用反引号 `` 和插值语法 ${} 可以轻松地将代码与文本混合:-)

于 2020-12-11T18:50:54.707 回答
0

我首选的方法是只使用一个 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');

检查这个 TS 游乐场

于 2021-09-24T09:36:26.340 回答