In TypeScript, the const
keyword cannot be used to declare class properties. Doing so causes the compiler to an error with "A class member cannot have the 'const' keyword."
I find myself in need to clearly indicate in code that a property should not be changed. I want the IDE or compiler to error if I attempt to assign a new value to the property once it has been declared. How do you guys achieve this?
I'm currently using a read-only property, but I'm new to Typescript (and JavaScript) and wonder whether there is a better way:
get MY_CONSTANT():number {return 10};
I'm using typescript 1.8. Suggestions?
PS: I'm now using typescript 2.0.3, so I've accepted David's answer
TypeScript 2.0 has the readonly
modifier:
class MyClass {
readonly myReadOnlyProperty = 1;
myMethod() {
console.log(this.myReadOnlyProperty);
this.myReadOnlyProperty = 5; // error, readonly
}
}
new MyClass().myReadOnlyProperty = 5; // error, readonly
It's not exactly a constant because it allows assignment in the constructor, but that's most likely not a big deal.
Alternative Solution
An alternative is to use the static
keyword with 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
This has the benefit of not being assignable in the constructor and only existing in one place.
Constants can be declare outside of classes and use within your class. Otherwise the get
property is a nice workaround
const MY_CONSTANT: string = "wazzup";
export class MyClass {
public myFunction() {
alert(MY_CONSTANT);
}
}
get
property very appropriate in your case
export const
and then import it from another file. Would be pretty easy to test though. Just declare a const
in one file, and try to use it in another without export/import, or to use it from the browser console.
You can mark properties with readonly
modifier in your declaration:
export class MyClass {
public static readonly MY_PUBLIC_CONSTANT = 10;
private static readonly myPrivateConstant = 5;
}
@see TypeScript Deep Dive book - Readonly
Angular 2 Provides a very nice feature called as Opaque Constants. Create a class & Define all the constants there using opaque constants.
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/"
};
Inject it in providers in app.module.ts
You will be able to use it across every components.
EDIT for Angular 4 :
For Angular 4 the new concept is Injection Token & Opaque token is Deprecated in Angular 4.
Injection Token Adds functionalities on top of Opaque Tokens, it allows to attach type info on the token via TypeScript generics, plus Injection tokens, removes the need of adding @Inject
Example Code
Angular 2 Using Opaque Tokens
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 Using Injection Tokens
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!
]
}
]
Injection tokens are designed logically on top of Opaque tokens & Opaque tokens are deprecated in Angular 4.
All of the replies with readonly
are only suitable when this is a pure TS environment - if it's ever being made into a library then this doesn't actually prevent anything, it just provides warnings for the TS compiler itself.
Static is also not correct - that's adding a method to the Class, not to an instance of the class - so you need to address it directly.
There are several ways to manage this, but the pure TS way is to use a getter
- exactly as you have done already.
The alternative way is to put it in as readonly
, but then use Object.defineProperty
to lock it - this is almost the same thing that is being done via the getter, but you can lock it to have a value, rather than a method to use to get it -
class MyClass {
MY_CONSTANT = 10;
constructor() {
Object.defineProperty(this, "MY_CONSTANT", {value: this.MY_CONSTANT});
}
}
The defaults make it read-only, but check out the docs for more details.
For this you can use the readonly
modifier. Object properties which are readonly
can only be assigned during initialization of the object.
Example in classes:
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.
Example in Object literals:
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
It's also worth knowing that the readonly
modifier is purely a typescript construct and when the TS is compiled to JS the construct will not be present in the compiled JS. When we are modifying properties which are readonly the TS compiler will warn us about it (it is valid JS).
Either use readOnly modifier with the constant one needs to declare or one might declare a constant outside the class and use it specifically only in the required class using get operator.
If what you want is to encapsulate constants, it doesn't have to be a class, it can be a namespace, here are the different options:
const MY_CONST_1 = "MyText"
This is the most optimal option and generates the following js:
const MY_CONST_1 = "MyText"
other opcion is encapsulate in namespace:
namespace Constants {
export const MY_CONST_1: string = 'MyText';
}
this generate the following js:
var Constants;
(function (Constants) {
Constants.MY_CONST_1 = 'MyText';
})(Constants || (Constants = {}));
and other options with class:
abstract class ConstantsClass {
static readonly MY_CONST_1 = "MyText";
}
this generate the following js:
class ConstantsClass {
}
ConstantsClass.MY_CONST_1 = "MyText";
you can choose the best for you.
You could also simply declare in lets say: src/classes/Car/consts/Honda.ts
export const CIVIC: string = "Civic";
export default {
CIVIC: CIVIC
};
Then in your class file do something like:
import Honda from "./consts/Honda";
export class Car {
protected model: string = Honda.Civic;
[...]
}
export default Car;
This way, you are making sure it's a constant. This method also works like a charm if you are building a dataset.
For me none of earlier answer works. I did need to convert my static class to enum. Like this:
export enum MyConstants {
MyFirstConstant = 'MyFirstConstant',
MySecondConstant = 'MySecondConstant'
}
Then in my component I add new property as suggested in other answers
export class MyComponent {
public MY_CONTANTS = MyConstans;
constructor() { }
}
Then in my component's template I use it this way
<div [myDirective]="MY_CONTANTS.MyFirstConstant"> </div>
EDIT: Sorry. My problem was different than OP's. I still leave this here if someelse have same problem than I.
Success story sharing
export
keyword beforeclass
as well aspublic static
before thereadonly
keyword. See here: stackoverflow.com/a/22993349export
(external modules) andpublic
keyword are unrelated to this question/answer, but on the topic of explicitness, I personally find it extremely easy to tell that a member is public when the keyword doesn't exist. I don't bother with it for that reason and because it adds more noise & is needless typing. It also makes the public members more distinct from ones marked asprivate
orprotected
. Anyway, just my opinion :)static readonly myReadOnlyProperty
when class is declared withexport default class { ... }
? Tried this.myVar, self.myVar, static, default... not working... (EDIT: default.myVar seems to be the solution, but I'm getting a type error)