Typescript: Use const enum values as keys of object literals

Created on 16 Mar 2017  路  13Comments  路  Source: microsoft/TypeScript

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}
Awaiting More Feedback Suggestion

Most helpful comment

Does this do what you're after?

let obj = {
  [E.Aa]: "action a",
  [E.Bb]: "action b"
}

obj[1] === "action a"

All 13 comments

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);
Was this page helpful?
0 / 5 - 0 ratings