TypeScript中的公共静态const


185

TypeScript中是否存在诸如公共静态常量之类的东西?我有一堂课,看起来像:

export class Library {
  public static BOOK_SHELF_NONE: string = "None";
  public static BOOK_SHELF_FULL: string = "Full";
}

在那堂课中,我可以做Library.BOOK_SHELF_NONE,而tsc不会抱怨。但是,如果我尝试在其他地方使用类库,并尝试做同样的事情,它将无法识别。


1
您能否展示在其他地方使用该类的示例?
raina77ow 2014年

@ raina77ow当我开始添加有关如何使用它的代码时,我意识到这是因为tsc抱怨命名空间。如果您想回答这个问题,我可以将其标记为已完成,因为您已经帮助我找到了它。我不太喜欢回答自己的问题。
2014年

啊,那是原因。很高兴我帮助了;我将添加显示编译结果的答案。
raina77ow 2014年

1
我还要补充一点readonly,如果您尝试重新分配这些字段,请确保TypeScript会警告您:public static readonly BOOK_SHELF_FULL
Jan Molak

Answers:


26

这是通过TS Playground编译成的TS代码段的内容:

define(["require", "exports"], function(require, exports) {
    var Library = (function () {
        function Library() {
        }
        Library.BOOK_SHELF_NONE = "None";
        Library.BOOK_SHELF_FULL = "Full";
        return Library;
    })();
    exports.Library = Library;
});

如您所见,定义为的两个属性public static都简单地附加到导出的函数(作为其属性)。因此,只要您正确访问该函数本身,就应该可以访问它们。


429

如果您确实希望某些东西在现代浏览器中表现得更像是一个静态常量(因为其他代码无法更改它),则可以getLibrary该类添加一个唯一的访问器(这仅适用于ES5 +浏览器和NodeJS) :

export class Library {
    public static get BOOK_SHELF_NONE():string { return "None"; }
    public static get BOOK_SHELF_FULL():string { return "Full"; }   
}

var x = Library.BOOK_SHELF_NONE;
console.log(x);
Library.BOOK_SHELF_NONE = "Not Full";
x = Library.BOOK_SHELF_NONE;
console.log(x);

如果运行它,您将看到将BOOK_SHELF_NONE属性设置为新值的尝试不起作用。

2.0

在TypeScript 2.0中,您可以使用readonly实现非常相似的结果:

export class Library {
    public static readonly BOOK_SHELF_NONE = "None";
    public static readonly BOOK_SHELF_FULL = "Full";
}

语法更简单,更明显。但是,编译器会阻止更改,而不是运行时间(与第一个示例不同,如示例所示,完全不允许更改)。


26
这是一个很好的答案。因此,我的编码变得更快乐。
gonzofish

7
但是,此解决方案有一个缺点-您不能将这些动态值用作类型约束,例如:type MyType: Library.BOOK_SHELF_NONE | Library.BOOK_SHELF_FULL;
Kostiantyn Kolesnichenko

2
@AdrianMoisa-请提出一个新问题。
WiredPrairie

2
@ hex-请提出一个新问题。
WiredPrairie

1
枚举呢?
curious95 '18

40

您可以使用命名空间进行操作,如下所示:

export namespace Library {
    export const BOOK_SHELF_NONE: string = 'NONE';
}

然后,您可以从其他任何地方导入它:

import {Library} from './Library';
console.log(Library.BOOK_SHELF_NONE);

如果您需要一个类,也可以将其包含在名称空间中: export class Book {...}


2
如果我在对象的初始化中使用常量,则似乎无法识别它们。例如:{ type: Library.BOOK_SHELF_NONE }似乎认为名称空间没有名为的导出BOOK_SHELF_NONE。但是,如果我仅使用引用设置局部变量,它将很好地解决。TS 2.2
粉碎

13

同时,可以通过将装饰器与Object.freeze或结合使用来解决此问题Object.defineProperty,我正在使用它,这比使用大量吸气剂要漂亮一些。您可以直接复制/粘贴此TS Playground来查看其运行情况。-有两种选择


将各个字段设为“最终”

以下装饰器将带注释的静态和非静态字段都转换为“ getter-only-properties”。

注意:如果注释了没有初始值的实例变量@final,则第一个分配的值(无论何时)将是最后一个。

// example
class MyClass {
    @final
    public finalProp: string = "You shall not change me!";

    @final
    public static FINAL_FIELD: number = 75;

    public static NON_FINAL: string = "I am not final."
}

var myInstance: MyClass = new MyClass();
myInstance.finalProp = "Was I changed?";
MyClass.FINAL_FIELD = 123;
MyClass.NON_FINAL = "I was changed.";

console.log(myInstance.finalProp);  // => You shall not change me!
console.log(MyClass.FINAL_FIELD);   // => 75
console.log(MyClass.NON_FINAL);     // => I was changed.

装饰器:确保在代码中包含此内容!

/**
* Turns static and non-static fields into getter-only, and therefor renders them "final".
* To use simply annotate the static or non-static field with: @final
*/
function final(target: any, propertyKey: string) {
    const value: any = target[propertyKey];
    // if it currently has no value, then wait for the first setter-call
    // usually the case with non-static fields
    if (!value) {
        Object.defineProperty(target, propertyKey, {
            set: function (value: any) {
                Object.defineProperty(this, propertyKey, {
                    get: function () {
                        return value;
                    },
                    enumerable: true,
                    configurable: false
                });
            },
            enumerable: true,
            configurable: true
        });
    } else { // else, set it immediatly
        Object.defineProperty(target, propertyKey, {
            get: function () {
                return value;
            },
            enumerable: true
        });
    }
}

作为上述装饰器的替代方案,还将有一个严格的版本,当有人尝试通过设置将某些值分配给该字段时,它甚至会引发错误"use strict";。(尽管这只是静态部分)

/**
 * Turns static fields into getter-only, and therefor renders them "final".
 * Also throws an error in strict mode if the value is tried to be touched.
 * To use simply annotate the static field with: @strictFinal
 */
function strictFinal(target: any, propertyKey: string) {
    Object.defineProperty(target, propertyKey, {
        value: target[propertyKey],
        writable: false,
        enumerable: true
    });
}

使每个静态字段“最终”

可能的缺点:这仅适用于该类的所有静态变量,也不适用于所有静态变量,但不能应用于特定的静态变量。

/**
* Freezes the annotated class, making every static 'final'.
* Usage:
* @StaticsFinal
* class MyClass {
*      public static SOME_STATIC: string = "SOME_STATIC";
*      //...
* }
*/
function StaticsFinal(target: any) {
    Object.freeze(target);
}
// Usage here
@StaticsFinal
class FreezeMe {
    public static FROZEN_STATIC: string = "I am frozen";
}

class EditMyStuff {
    public static NON_FROZEN_STATIC: string = "I am frozen";
}

// Test here
FreezeMe.FROZEN_STATIC = "I am not frozen.";
EditMyStuff.NON_FROZEN_STATIC = "I am not frozen.";

console.log(FreezeMe.FROZEN_STATIC); // => "I am frozen."
console.log(EditMyStuff.NON_FROZEN_STATIC); // => "I am not frozen."

我认为这是最干净的方法。谢谢!
锡伯斯(Thibs)2016年

一个小的补充:if (!value)应该将if (value === undefined)静态值设为""0
Leon Adler

6

谢谢WiredPrairie!

只是为了扩展您的答案,这是定义常量类的完整示例。

// CYConstants.ts

class CYConstants {
    public static get NOT_FOUND(): number    { return -1; }
    public static get EMPTY_STRING(): string { return ""; }
}

export = CYConstants;

使用

// main.ts

import CYConstants = require("./CYConstants");

console.log(CYConstants.NOT_FOUND);    // Prints -1
console.log(CYConstants.EMPTY_STRING); // Prints "" (Nothing!)

2

从TS 1.7.5开始,以下解决方案也适用。

// Constancts.ts    
export const kNotFoundInArray = -1;
export const AppConnectionError = new Error("The application was unable to connect!");
export const ReallySafeExtensions = ["exe", "virus", "1337h4x"];

使用方法:

// Main.ts    
import {ReallySafeExtensions, kNotFoundInArray} from "./Constants";

if (ReallySafeExtensions.indexOf("png") === kNotFoundInArray) {
    console.log("PNG's are really unsafe!!!");
}

这确实很酷,但是如果您使用它来定义多个值(例如您可能在其他语言的Enum中使用),那么您将无法单独导入所有值。这可能很快变得很麻烦!
科里·拉森

100%同意。可能能够为它们命名空间,但我尚未对其进行研究。也许您可以从“ ./Constants.ts”导入{ArrayConstants},然后执行类似ArrayConstants.NotFound的操作。我有时间的话,必须多玩一点。
安德鲁

是的,您可以使用命名空间来避免单独使用值,请在此处阅读我的答案。
伊万·卡斯泰拉诺斯

0

只需在类中简单地“导出”变量和“导入”

export var GOOGLE_API_URL = 'https://www.googleapis.com/admin/directory/v1';

// default err string message
export var errStringMsg = 'Something went wrong';

现在将其用作

import appConstants = require('../core/AppSettings');
console.log(appConstants.errStringMsg);
console.log(appConstants.GOOGLE_API_URL);

0

您可以使用吸气剂,以便您的媒体资源只能读取。例:

export class MyClass {
    private _LEVELS = {
        level1: "level1",
        level2: "level2",
        level2: "level2"
    };

    public get STATUSES() {
        return this._LEVELS;
    }
}

在另一类中使用:

import { MyClass } from "myclasspath";
class AnotherClass {
    private myClass = new MyClass();

    tryLevel() {
       console.log(this.myClass.STATUSES.level1);
    }
}

这使人们期望它可以不断写入:let a = new MyClass(); a.STATUSES.level1 = 'not level1'; let plainWrong = new MyClass().STATUSES.level1; // 'not level1'
Leon Adler
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.