【问题标题】:How to implement class constants?如何实现类常量?
【发布时间】:2016-09-12 21:55:10
【问题描述】:

在 TypeScript 中,const 关键字不能用于声明类属性。这样做会导致编译器出现“类成员不能具有 'const' 关键字”的错误。

我发现自己需要在代码中明确指出不应更改属性。如果我在声明属性后尝试为其分配新值,我希望 IDE 或编译器出错。你们是如何做到这一点的?

我目前使用的是只读属性,但我是 Typescript(和 JavaScript)的新手,想知道是否有更好的方法:

get MY_CONSTANT():number {return 10};

我正在使用打字稿 1.8。有什么建议吗?

PS:我现在使用的是 typescript 2.0.3,所以我接受了David's answer

【问题讨论】:

    标签: typescript class-constants


    【解决方案1】:

    常量可以在类之外声明并在你的类中使用。否则,get 属性是一个很好的解决方法

    const MY_CONSTANT: string = "wazzup";
    
    export class MyClass {
    
        public myFunction() {
    
            alert(MY_CONSTANT);
        }
    }
    

    【讨论】:

    • 谢谢;我担心这个实现,因为它不是可移植的(在模型中,常量实际上不是类的一部分)并且它将信息泄漏到更大的范围内,但它具有成为真正常量的优势,所以我赢了不能在不敲响警钟的情况下改变它。
    • 我理解这种担忧,我发现在您的情况下使用 get 属性非常合适
    • Per angular.io/docs/ts/latest/guide/style-guide.html 请使用驼峰大写字母而不是大写字母。不建议使用大写的常量。
    • Angular 样式指南,而不是 TypeScript 样式指南。问题是关于 TypeScript 的具体问题
    • @Esko 我相信在打字稿中 const 仅限于文件,因为每个文件都是一个模块。要使其在外部可访问,您需要使用 export const 声明它,然后从另一个文件中导入它。虽然很容易测试。只需在一个文件中声明const,然后尝试在另一个文件中使用它而不导出/导入,或者从浏览器控制台使用它。
    【解决方案2】:

    TypeScript 2.0 有 readonly modifier:

    class MyClass {
        readonly myReadOnlyProperty = 1;
    
        myMethod() {
            console.log(this.myReadOnlyProperty);
            this.myReadOnlyProperty = 5; // error, readonly
        }
    }
    
    new MyClass().myReadOnlyProperty = 5; // error, readonly
    

    它并不完全是一个常量,因为它允许在构造函数中赋值,但这很可能没什么大不了的。

    替代解决方案

    另一种方法是将static 关键字与readonly 一起使用:

    class MyClass {
        static readonly myReadOnlyProperty = 1;
    
        constructor() {
            MyClass.myReadOnlyProperty = 5; // error, readonly
        }
    
        myMethod() {
            console.log(MyClass.myReadOnlyProperty);
            MyClass.myReadOnlyProperty = 5; // error, readonly
        }
    }
    
    MyClass.myReadOnlyProperty = 5; // error, readonly
    

    这样做的好处是不能在构造函数中赋值,并且只存在于一个地方。

    【讨论】:

    • 要从类外部访问属性,您需要在class 之前添加export 关键字以及在readonly 关键字之前添加public static。见这里:stackoverflow.com/a/22993349
    • 问题。不知道为什么需要类名才能在类本身中使用该 readOnly 属性? 'MyClass.myReadonlyProperty'
    • @SaiyaffFarouk 如果我理解你的问题,答案是静态属性作为类的一部分存在,而不是在类的实例上。因此,您使用类名而不是包含类实例的变量来访问它们。
    • export(外部模块)和public 关键字与此问题/答案无关,但在明确性的主题上,我个人发现在以下情况下判断成员是公开的非常容易关键字不存在。出于这个原因,我不打扰它,因为它增加了更多的噪音并且是不必要的打字。它还使公共成员与标记为privateprotected 的成员更加不同。无论如何,只是我的意见:)
    • 匿名类呢?当使用 export default class { ... } 声明类时,有关如何访问 static readonly myReadOnlyProperty 的任何想法?试过 this.myVar, self.myVar, static, default... 不工作... (编辑:default.myVar 似乎是解决方案,但我收到类型错误)
    【解决方案3】:

    Angular 2 提供了一个非常好的特性,称为不透明常量。 创建一个类并使用不透明的常量在那里定义所有常量。

    import { OpaqueToken } from "@angular/core";
    
    export let APP_CONFIG = new OpaqueToken("my.config");
    
    export interface MyAppConfig {
        apiEndpoint: string;
    }
    
    export const AppConfig: MyAppConfig = {    
        apiEndpoint: "http://localhost:8080/api/"    
    };
    

    将其注入提供程序中 app.module.ts

    您将能够在每个组件中使用它。

    编辑 Angular 4:

    对于 Angular 4,新概念是注入令牌和不透明令牌在 Angular 4 中已弃用。

    Injection Token 在 Opaque Tokens 之上添加功能,它允许通过 TypeScript 泛型在令牌上附加类型信息,加上 Injection 令牌,无需添加 @Inject

    示例代码

    Angular 2 使用不透明标记

    const API_URL = new OpaqueToken('apiUrl'); //no Type Check
    
    
    providers: [
      {
        provide: DataService,
        useFactory: (http, apiUrl) => {
          // create data service
        },
        deps: [
          Http,
          new Inject(API_URL) //notice the new Inject
        ]
      }
    ]
    

    Angular 4 使用注入令牌

    const API_URL = new InjectionToken<string>('apiUrl'); // generic defines return value of injector
    
    
    providers: [
      {
        provide: DataService,
        useFactory: (http, apiUrl) => {
          // create data service
        },
        deps: [
          Http,
          API_URL // no `new Inject()` needed!
        ]
      }
    ]
    

    注入令牌在逻辑上设计在不透明令牌之上,而不透明令牌在 Angular 4 中已被弃用。

    【讨论】:

    • 减一。这个问题与 Angular 无关。它正在请求 TypeScript 解决方案。
    【解决方案4】:

    您可以在声明中使用readonly 修饰符标记属性:

    export class MyClass {
      public static readonly MY_PUBLIC_CONSTANT = 10;
      private static readonly myPrivateConstant = 5;
    }
    

    @见TypeScript Deep Dive book - Readonly

    【讨论】:

      【解决方案5】:

      要么对需要声明的常量使用 readOnly 修饰符,要么可能在类外部声明一个常量,然后使用 get 运算符仅在所需类中专门使用它。

      【讨论】:

        【解决方案6】:

        为此,您可以使用readonly 修饰符。 readonly 的对象属性只能在对象初始化期间分配。

        类中的示例:

        class Circle {
          readonly radius: number;
        
          constructor(radius: number) {
            this.radius = radius;
          }
        
          get area() {
            return Math.PI * this.radius * 2;
          }
        }
        
        const circle = new Circle(12);
        circle.radius = 12; // Cannot assign to 'radius' because it is a read-only property.
        

        对象字面量示例:

        type Rectangle = {
          readonly height: number;
          readonly width: number;
        };
        
        const square: Rectangle = { height: 1, width: 2 };
        square.height = 5 // Cannot assign to 'height' because it is a read-only property
        

        还值得知道readonly 修饰符纯粹是一个打字稿构造,当 TS 编译为 JS 时,该构造将不会出现在编译的 JS 中。当我们修改只读属性时,TS 编译器会警告我们(它是有效的 JS)。

        【讨论】:

          【解决方案7】:

          对我来说,早期的答案都不起作用。我确实需要将我的静态类转换为枚举。 像这样:

          export enum MyConstants {
            MyFirstConstant = 'MyFirstConstant',
            MySecondConstant = 'MySecondConstant'
          }
          

          然后在我的组件中添加其他答案中建议的新属性

          export class MyComponent {
          public MY_CONTANTS = MyConstans;
          constructor() { }
          }
          

          然后在我的组件模板中我这样使用它

          <div [myDirective]="MY_CONTANTS.MyFirstConstant"> </div>
          

          编辑:对不起。我的问题与 OP 的不同。如果有人和我有同样的问题,我还是把它留在这里。

          【讨论】:

          • 使用枚举来保存常量在任何语言中都不是一个好习惯。
          • 这是目前可用解决方案的最佳解决方案。我知道这是不应该使用枚举的方式,但是对于 Angular,它是拥有可绑定常量的最干净的方式。
          【解决方案8】:

          所有带有readonly 的回复仅适用于纯 TS 环境——如果它被制成库,那么这实际上并不能阻止任何事情,它只是为 TS 编译器本身提供警告。

          静态也不正确 - 那是向类添加方法,而不是向类的 实例 - 所以你需要直接解决它。

          有几种方法可以管理此问题,但纯粹的 TS 方法是使用 getter - 就像您已经做过的那样。

          另一种方法是将其作为readonly 放入,然后使用Object.defineProperty 锁定它——这与通过getter 所做的几乎相同,但您可以将其锁定为具有值,而不是用来获取它的方法 -

          class MyClass {
              MY_CONSTANT = 10;
          
              constructor() {
                  Object.defineProperty(this, "MY_CONSTANT", {value: this.MY_CONSTANT});
              }
          }
          

          默认设置为只读,但请查看docs 了解更多详细信息。

          【讨论】:

            猜你喜欢
            • 2019-06-09
            • 1970-01-01
            • 2012-02-07
            • 2010-12-28
            • 1970-01-01
            • 2020-05-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多