public static const in TypeScript

前端 未结 8 842
慢半拍i
慢半拍i 2020-12-07 10:11

Is there such a thing as public static constants in TypeScript? I have a class that looks like:

export class Library {
  public static BOOK_SHELF_NONE: stri         


        
相关标签:
8条回答
  • 2020-12-07 10:40

    The following solution also works as of 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"];
    

    To use:

    // Main.ts    
    import {ReallySafeExtensions, kNotFoundInArray} from "./Constants";
    
    if (ReallySafeExtensions.indexOf("png") === kNotFoundInArray) {
        console.log("PNG's are really unsafe!!!");
    }
    
    0 讨论(0)
  • 2020-12-07 10:42

    You can use a getter, so that your property is going to be reading only. Example:

    export class MyClass {
        private _LEVELS = {
            level1: "level1",
            level2: "level2",
            level2: "level2"
        };
    
        public get STATUSES() {
            return this._LEVELS;
        }
    }
    

    Used in another class:

    import { MyClass } from "myclasspath";
    class AnotherClass {
        private myClass = new MyClass();
    
        tryLevel() {
           console.log(this.myClass.STATUSES.level1);
        }
    }
    
    0 讨论(0)
  • 2020-12-07 10:43

    Thank you WiredPrairie!

    Just to expand on your answer a bit, here is a complete example of defining a constants class.

    // CYConstants.ts
    
    class CYConstants {
        public static get NOT_FOUND(): number    { return -1; }
        public static get EMPTY_STRING(): string { return ""; }
    }
    
    export = CYConstants;
    

    To use

    // main.ts
    
    import CYConstants = require("./CYConstants");
    
    console.log(CYConstants.NOT_FOUND);    // Prints -1
    console.log(CYConstants.EMPTY_STRING); // Prints "" (Nothing!)
    
    0 讨论(0)
  • 2020-12-07 10:48

    If you did want something that behaved more like a static constant value in modern browsers (in that it can't be changed by other code), you could add a get only accessor to the Library class (this will only work for ES5+ browsers and 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);
    

    If you run it, you'll see how the attempt to set the BOOK_SHELF_NONE property to a new value doesn't work.

    2.0

    In TypeScript 2.0, you can use readonly to achieve very similar results:

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

    The syntax is a bit simpler and more obvious. However, the compiler prevents changes rather than the run time (unlike in the first example, where the change would not be allowed at all as demonstrated).

    0 讨论(0)
  • 2020-12-07 10:49

    Here's what's this TS snippet compiled into (via TS Playground):

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

    As you see, both properties defined as public static are simply attached to the exported function (as its properties); therefore they should be accessible as long as you properly access the function itself.

    0 讨论(0)
  • 2020-12-07 10:51

    Meanwhile this can be solved through a decorator in combination with Object.freeze or Object.defineProperty, I'm using this, it's a little bit prettier than using tons of getters. You can copy/paste this directly TS Playground to see it in action. - There are two options


    Make individual fields "final"

    The following decorator converts both, annotated static and non-static fields to "getter-only-properties".

    Note: If an instance-variable with no initial value is annotated @final, then the first assigned value (no matter when) will be the final one.

    // 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.
    

    The Decorator: Make sure you include this in your code!

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

    As an alternative to the decorator above, there would also be a strict version of this, which would even throw an Error when someone tried to assign some value to the field with "use strict"; being set. (This is only the static part though)

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

    Make every static field "final"

    Possible Downside: This will only work for ALL statics of that class or for none, but cannot be applied to specific statics.

    /**
    * 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."
    
    0 讨论(0)
提交回复
热议问题