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?

2

18 Answers

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.

6
class Whatever { static get MyConst() { return 10; } } let a = Whatever.MyConst; 

Seems to work for me.

6

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-3

8

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

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

2

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; //... } } 
1

You can create a way to define static constants on a class using an odd feature of ES6 classes. Since statics are inherited by their subclasses, you can do the following:

const withConsts = (map, BaseClass = Object) => { class ConstClass extends BaseClass { } Object.keys(map).forEach(key => { Object.defineProperty(ConstClass, key, { value: map[key], writable : false, enumerable : true, configurable : false }); }); return ConstClass; }; class MyClass extends withConsts({ MY_CONST: 'this is defined' }) { foo() { console.log(MyClass.MY_CONST); } } 
1

Like said, I think you're looking for Object.freeze(). Here's an example of a class with immutable statics:

class User { constructor(username, age) { if (age < User.minimumAge) { throw new Error('You are too young to be here!'); } this.username = username; this.age = age; this.state = 'active'; } } User.minimumAge = 16; User.validStates = ['active', 'inactive', 'archived']; deepFreeze(User); function deepFreeze(value) { if (typeof value === 'object' && value !== null) { Object.freeze(value); Object.getOwnPropertyNames(value).forEach(property => { deepFreeze(value[property]); }); } return value; } 

I did this.

class Circle { constuctor(radius) { this.radius = radius; } static get PI() { return 3.14159; } } 

The value of PI is protected from being changed since it is a value being returned from a function. You can access it via Circle.PI. Any attempt to assign to it is simply dropped on the floor in a manner similar to an attempt to assign to a string character via [].

You can make the "constants" read-only (immutable) by freezing the class. e.g.

class Foo { static BAR = "bat"; //public static read-only } Object.freeze(Foo); /* Uncaught TypeError: Cannot assign to read only property 'BAR' of function 'class Foo { static BAR = "bat"; //public static read-only }' */ Foo.BAR = "wut"; 
1

Here is one more way you can do

/* one more way of declaring constants in a class, Note - the constants have to be declared after the class is defined */ class Auto{ //other methods } Auto.CONSTANT1 = "const1"; Auto.CONSTANT2 = "const2"; console.log(Auto.CONSTANT1) console.log(Auto.CONSTANT2);

Note - the Order is important, you cannot have the constants above

Usage

console.log(Auto.CONSTANT1); 
1

You could use import * as syntax. Although not a class, they are real const variables.

Constants.js

export const factor = 3; export const pi = 3.141592; 

index.js

import * as Constants from 'Constants.js' console.log( Constants.factor ); 

The cleanest way I've found of doing this is with TypeScript - see How to implement class constants?

class MyClass { static readonly CONST1: string = "one"; static readonly CONST2: string = "two"; static readonly CONST3: string = "three"; } 
1

If trying to make a const/variable static to a class; try using the hash (#) to define a place holder, than a function to access it.

class Region { // initially empty, not accessible from outside static #empty_region = null; /* Make it visible to the outside and unchangeable [note] created on first call to getter. */ static EMPTY() { if (!this.#empty_region) this.#empty_region = new Region(0, 0, 0, 0); return this.#empty_region; } #reg = {x0:0, y0:0, x1:0, y1:0}; constructor(x0, y0, x1, y1) { this.setRegion(x0, y0, x1, y1); } // setters/getters } 

Implementation:

let someRegion = Region.EMPTY(); let anotherRegion = Region.EMPTY(); 

If you are comfortable mixing and matching between function and class syntax you can declare constants after the class (the constants are 'lifted') . Note that Visual Studio Code will struggle to auto-format the mixed syntax, (though it works).

class MyClass { // ... } MyClass.prototype.consts = { constant1: 33, constant2: 32 }; mc = new MyClass(); console.log(mc.consts.constant2); 

Adding up to other answers you need to export the class to use in a different class. This is a typescript version of it.

//Constants.tsx const DEBUG: boolean = true; export class Constants { static get DEBUG(): boolean { return DEBUG; } } //Anotherclass.tsx import { Constants } from "Constants"; if (Constants.DEBUG) { console.log("debug mode") }

Here You Go!

const Status = Object.freeze(class Status { static Disabled = 0 static Live = 1 }) 

Just declare your variables as private and use a get method to retrieve them.

class MyClass { #myConst = 'Something'; static #anotherConst = 'Something Else'; get myConst() { return this.#myConst; // instance method } static get anotherConst() { return MyClass.#anotherConst; // static method } } let myClass = new MyClass(); console.log( myClass.myConst + ' is not ' + MyClass.anotherConst ); 

Users cannot change the original variable, and you can write the class to use the get methods rather than the private variables themselves.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy