I want to use values of a const enum to define object literals
* Syntactic *
const enum E {
Aa = 1, Bb = 2
}
let obj = {
E.Aa: "action a",
E.Bb: "action b",
};
Emit
let obj = {
1: "action a",
2: "action b",
};
Compatibility
Since JS supports string literals keys, and strings containing .
is not a valid id, we may limit the usage of const enums to "EnumTypeName
.EnumValueName
" to avoid ambiguities. And then,
{ e_Aa: 1 }
is { "e_Aa": 1}
{ E.Aa: 1 }
is { 1: 1}
{ "E.Aa": 1 }
is { "E.Aa": 1}
Does this do what you're after?
let obj = {
[E.Aa]: "action a",
[E.Bb]: "action b"
}
obj[1] === "action a"
But {[E.Aa]: 1}
seems to cost more when executing, compared to {1: 1}
. The code inside [
and ]
is an expression which needs to be computed when executed.
And more importantly, var a = {[E.Aa]: 1}
is converted by tsc
into (target is ES5
):
var a = (_a = {},
_a[1] = 1,
_a);
var _a;
Too ugly.
Currently, with TypeScript-2.3.0-dev.20170307
const enum E {
Aa = 1
}
var a = {
[E.Aa]: 1
};
tsconfig.json
is
{
"compilerOptions": {
"preserveConstEnums": false,
"target": "es5"
}
}
And its output is:
var a = (_a = {},
_a[1 /* Aa */] = 1,
_a);
var _a;
So I think it's too ugly and inconvenient to use [E.Aa]
.
This is standard ES6 syntax. Not that the 'ugliness' of output code should really be a concern, but if you change your compiler settings, to target=ES2015
, the output is exactly as the input.
// output
var a = {
[E.Aa]: 1
};
So I think it's too ugly and inconvenient to use
[E.Aa]
.
Two extra characters are ugly and inconvenient?
This is part of the ECMAScript standard and is required to inform the run-time compiler that you are dealing with the value of a variable versus a string label for the property name. Otherwise how would the runtime be able to determine that. If you think it is ugly, you would need to champion TC39 to change the ECMAScript standards.
Well, [E.Aa]
is only inconvenient, but not ugly. I just think the output of ES5 version is ugly.
If using the grammar like { E.Aa: 1 }
, there's little ambiguity and the code can skip some unnecessary run-time computation.
If I set target: "es6"
in tsconfig.json
, then tsc
makes:
var a = {
[1 /* Aa */]: 1
};
But the code breaks uglifyjs
, and Babel
converts it into:
"use strict";
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var a = _defineProperty({}, 1 /* Aa */, 1);
I just want an elegant wayt to declare literals which is also elegant under ES5.
If you start worrying about what your code looks like when you emit, you will be serially disappointed.
Do you have a suggestion of a better emit?
Just for a better output, tsc
may does constant folding and remove the [
and ]
if possible - at least "const enum" values and number literals are surely constants.
Just for a better output
Like what? Write what you would expect to be outputted.
If there's constant folding, then
var a = {
[E.Aa | E.Bb]: 'action ab'
}
should output (the code below has been updated)
var a = {
3 /* E.Aa | E.Bb */: 'action ab'
}
If tsc
is not so smart, I want var a = { [E.Aa]: 1 }
to always output var a = { 1: 1 }
whatever the compileOptions.target
is.
Added: as for my expectation, I want tsc
to compile var a = { E.Aa: 1 }
and output var a = { 1: 1 }
.
Related, TypeScript currently compiles:
const x = {
['a']: 'foo'
}
const y = {
[1]: 'foo'
}
as
var x = (_a = {},
_a['a'] = 'foo',
_a);
var y = (_b = {},
_b[1] = 'foo',
_b);
var _a, _b;
Both arguments are literals and the 'computed property' aspect of them should either be dropped or emit a compiler warning about using a computed property with a literal. A const enum
value should function exactly as its corresponding literal value, so the behavior should likely be fixed for all forms of literals. Essentially the "is this expression a compile-time constant" logic that the compiler needs in a few other places (such as for const enum
initializers) should be applicable here to replace computed properties with normal properties.
I just ran into this issue when trying to convert a tiny module that just holds some data in various representations to TypeScript. While not a deal breaker, it'd be more obvious what's going on (and potentially faster) if
export const ABC =1
export const Mapping = {
[ABC]: "ABC"
}
Would result in
export var ABC =1
export var Mapping = {
1: "ABC"
}
instead of the current
export var ABC =1
export var Mapping = (_a = {},
_a[1] = "Accepted",
_a);
Most helpful comment
Does this do what you're after?