I propose to either add a new ability for
the const keyword or use the reserved word final to make
non-writable object and class members.
Note: The TypeErrors are thrown following the behavior of Object.freeze or
a { configurable: false, writable: false } property descriptor. They
will be thrown in a "use strict"; environment, but usually not
otherwise.
Use final, since it is an unused reserved word, or possibly const to define constant Class or Object members:
class X { // const / final now makes static class properties // non-writable, and non-configurable static const abc = 123; static final abc = 123; // also works to make constant private properties const #def = 456; final #def = 456; // as well as constant normal class fields, if you're into that const ghi = 789; final ghi = 789; // and constant private / static / normal methods static const doSomething() { } static final doSomething() { } const #somethingPrivate() { } final #somethingPrivate() { } const cantChangeMe() { } final cantChangeMe() { } constructor(a = 3) { const somethingPrivate = this.#somethingPrivate; // throws new TypeError('Assignment to constant property.') this.#somethingPrivate = function () { return a; } this.#somethingPrivate === somethingPrivate // true const def = this.#def; // throws new TypeError('Assignment to constant property.') this.#def = 1000; this.#def === def // true } } const x = new X(); const ghi = x.ghi; // throws new TypeError('Assignment to constant property.') x.ghi = 2000; x.ghi === ghi // true const doSomething = X.doSomething; // throws new TypeError('Assignment to constant property.') X.doSomething = function doSomethingElse() { }; X.doSomething === doSomething // true const cantChangeMe = X.prototype.cantChangeMe; // throws new TypeError('Assignment to constant property.') X.prototype.cantChangeMe = function yesICan() { }; X.prototype.cantChangeMe === cantChangeMe // true const o = { // const / final can be used in normal object literals, too const things() { }, final things() { }, const prop: 5, final prop: 5, }; const things = o.things; // throws new TypeError('Assignment to constant property.') o.things = function stuff() { }; o.things === things // true const prop = o.prop; // throws new TypeError('Assignment to constant property.') o.prop = 11; o.prop === prop // trueThis could be transpiled by simply using Object.defineProperty like so,
at least on public members:
Defining everything as final or const in a Class or Object doesn't quiet
do the job. What if we want true immutability?
I've also looked into some kind of special syntax for creating immutable
objects and making classes return immutable objects. Maybe we could use
final for that, as well?
This could be transpiled essentially by just using Object.freeze and Object.defineProperty:
const ConstantObject = (() => { class ConstantObject { static abc = 123; static doSomething() { } cantChangeMe() { } constructor() { this.d = 8; return Object.freeze(this); } } Object.defineProperty(ConstantObject, 'prototype', { value: Object.freeze(ConstantObject.prototype), }); return Object.freeze(ConstantObject); })(); const finalObject = Object.freeze({ prop: 5, things() { }, });What do you think? I'm currently leaning towards using the final keyword for everything.
Both sides have pros and cons.