Declaring static constants in ES6 classes?

Declaring static constants in ES6 classes?

I want to implement constants in a class, because that’s where it makes sense to locate them in the code.
So far, I have been implementing the following workaround with static methods:
class MyClass {
static constant1() { return 33; }
static constant2() { return 2; }
// …
}

I know there is a possibility to fiddle with prototypes, but many recommend against this.
Is there a better way to implement constants in ES6 classes?

Solutions/Answers:

Solution 1:

Here’s a few things you could do:

Export a const from the module. Depending on your use case, you could just:

export const constant1 = 33;

And import that from the module where necessary. Or, building on your static method idea, you could declare a static get accessor:

const constant1 = 33,
      constant2 = 2;
class Example {

  static get constant1() {
    return constant1;
  }

  static get constant2() {
    return constant2;
  }
}

That way, you won’t need parenthesis:

const one = Example.constant1;

Babel REPL Example

Then, as you say, since a class is just syntactic sugar for a function you can just add a non-writable property like so:

class Example {
}
Object.defineProperty(Example, 'constant1', {
    value: 33,
    writable : false,
    enumerable : true,
    configurable : false
});
Example.constant1; // 33
Example.constant1 = 15; // TypeError

It may be nice if we could just do something like:

class Example {
    static const constant1 = 33;
}

But unfortunately this class property syntax is only in an ES7 proposal, and even then it won’t allow for adding const to the property.

Related:  JavaScript: how to use a regular expression to remove blank lines from a string? [closed]

Solution 2:

I’m using babel and the following syntax is working for me:

class MyClass {
    static constant1 = 33;
    static constant2 = {
       case1: 1,
       case2: 2,
    };
    // ...
}

MyClass.constant1 === 33
MyClass.constant2.case1 === 1

Please consider that you need the preset "stage-0".
To install it:

npm install --save-dev babel-preset-stage-0

// in .babelrc
{
    "presets": ["stage-0"]
}

Update:

currently use stage-2

Solution 3:

class Whatever {
    static get MyConst() { return 10; }
}

let a = Whatever.MyConst;

Seems to work for me.

Solution 4:

In this document it states:

There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property

This means that it is intentionally like this.

Maybe you can define a variable in the constructor?

constructor(){
    this.key = value
}

Solution 5:

It is also possible to use Object.freeze on you class(es6)/constructor function(es5) object to make it immutable:

class MyConstants {}
MyConstants.staticValue = 3;
MyConstants.staticMethod = function() {
  return 4;
}
Object.freeze(MyConstants);
// after the freeze, any attempts of altering the MyConstants class will have no result
// (either trying to alter, add or delete a property)
MyConstants.staticValue === 3; // true
MyConstants.staticValue = 55; // will have no effect
MyConstants.staticValue === 3; // true

MyConstants.otherStaticValue = "other" // will have no effect
MyConstants.otherStaticValue === undefined // true

delete MyConstants.staticMethod // false
typeof(MyConstants.staticMethod) === "function" // true

Trying to alter the class will give you a soft-fail (won’t throw any errors, it will simply have no effect).

Related:  Convert XML to String and append to page

Solution 6:

Maybe just put all your constants in a frozen object?

class MyClass {

    constructor() {
        this.constants = Object.freeze({
            constant1: 33,
            constant2: 2,
        });
    }

    static get constant1() {
        return this.constants.constant1;
    }

    doThisAndThat() {
        //...
        let value = this.constants.constant2;
        //...
    }
}