In this blog post, a describe a little hack for quickly setting up constants (think enum values, but not wrapped inside a namespace). It is more an educational puzzle than something you should actually use in your code.
As an example, consider the following TypeScript enum (JavaScript itself does not have enums):
enum MyEnum { foo, bar, baz }
assert.equal(MyEnum.foo, 0);
assert.equal(MyEnum.bar, 1);
assert.equal(MyEnum.baz, 2);
I usually prefer to use strings as enum values, because they are easier to debug:
enum MyEnum { foo='foo', bar='bar', baz='baz' }
assert.equal(MyEnum.foo, 'foo');
assert.equal(MyEnum.bar, 'bar');
assert.equal(MyEnum.baz, 'baz');
You can achieve something similar in JavaScript as follows.
const keyProxy = new Proxy({}, {
get(_target, propKey, _receiver) {
return propKey;
}
});
const {foo, bar, baz} = keyProxy;
assert.equal(foo, 'foo');
assert.equal(bar, 'bar');
assert.equal(baz, 'baz');
How does it work? We combine two ingredients to achieve this effect.
First, the proxy is an object where, whatever key you use to read a property, you always get that key as a value:
assert.equal(keyProxy.hello, 'hello');
assert.equal(keyProxy.world, 'world');
Second, using property value shorthands during destructuring lets us specify both a property key and a variable name at the same time. That is, the following two declarations are equivalent.
const {foo, bar, baz} = keyProxy;
const {foo: foo, bar: bar, baz: baz} = keyProxy;
If you use symbols as values for the constants, you get more type safety. The only line of the proxy that changes is line A.
const keyProxy = new Proxy({}, {
get(_target, propKey, _receiver) {
return Symbol(propKey); // (A)
}
});
const {foo, bar, baz} = keyProxy;
assert.equal(typeof foo, 'symbol');
assert.equal(String(foo), 'Symbol(foo)');