In C#, an enum is a declaration that defines a distinct type consisting of a
set of named constant values of that type. The distinct type of the enum has an
underlying numeric base type, such as byte, int, long, etc. By default, each
constant value is assigned an incrementing integer value starting from 0,
although this sequence can be interrupted by assigning an explicit integer
value:
```cs
enum Colors { Red, Green, Blue }
Colors.Red // 0
Colors.Green // 1
Colors.Blue // 2
enum Days: byte { Sat = 1, Sun, Mon, Tue, Wed, Thu, Fri }
Days.Sat // 1
Days.Sun // 2
```
C# enum values can be used with many standard numeric operations, and are often
heavily used with bitwise operators. A C# enum value is a constant value, and
is internally treated as a numeric literal by the compiler. In any C# enum
declaration, only constant numeric values can be explicitly assigned to an enum
member, although constant reduction is permitted:
```cs
enum UriComponents {
Scheme = 0x1,
UserInfo = 0x2,
Host = 0x4,
Port = 0x8,
Path = 0x10,
Query = 0x20,
Fragment = 0x40,
AbsoluteUri = Scheme | UserInfo | Host | Port | Path | Query | Fragment
}
```
Although C# enum values are converted to numeric literals by the compiler,
their type information is preserved. This allows for an enum type to have
different behavior from a literal numeric type. One example of this behavior is
how ToString is handled on an enum value:
```cs
enum Numbers { Zero, One, Two }
(int)Numers.Zero // 0
Numbers.Zero.ToString() // Zero
```
The enum type itself has a number of static methods to make it easier to
program against, including: GetName, GetNames, GetUnderlyingType, IsDefined,
Parse, and ToObject. Instance members of an enum type include HasFlag and
CompareTo.
In TypeScript we treat an enum declaration in a fashion similar to a C# enum,
with respect to how we handle incremental integer values and explicitly
assigned values. We effectively emit an enum as an object literal:
```ts
// TypeScript
enum Colors { Red, Green, Blue }
Colors.Red // 0
// JavaScript
var Colors;
(function (Colors) {
Colors[Colors[0] = "Red"] = 0;
Colors[Colors[1] = "Green"] = 1;
Colors[Colors[2] = "Blue"] = 2;
})(Colors || (Colors = {}));
Colors.Red // 0
```
In this way, you can use `Colors.Red` to get the value 0, and `Colors[0]` to
get the value "Red". As a performance optimization we also have what we call
"const enums". A const enum can be completely erased by the compiler:
```ts
// TypeScript
const enum Colors { Red, Green, Blue }
Colors.Red // 0
// JavaScript
0 /*Colors.Red*/ // 0
```
I think a general proposal for ES enums would be a combination of the above
approaches, with some additions:
* An enum can be a declaration or an expression.
* The body of an enum consists of a new lexical scope.
* Enum members are standard JavaScript identifiers.
* Enum members are automatically assigned an incrementing integer value,
starting at zero.
* Enum members can be explicitly assigned to an integer value, or another enum
value.
* Within the body of an enum, Enum members can be accessed in the initializer
without qualification.
* Within the body of an enum, Enum members are lexically declared names and
cannot be accessed before they are defined (TDZ).
* An enum declaration can be called as a function to convert a string or
numeric value into the enum value, making enum types distinct from numbers and
from each other. [1]
* The result of `typeof` on an enum value is `enum`.
* Enum values support (at least) the following operators, returning an enum
value of the same type: + (unary), - (unary), ~, + (binary), - (binary), |
(binary), & (binary), ^ (binary).
* Any binary operation between two enums of different types is a TypeError. [1]
* Any binary operation between an enum and a number first converts the number
to the enum type. If the number is not an integer it is a TypeError.
* Any binary operation between an enum and a string first converts the enum
into the string value for that enum based on the enum member's JavaScript
identifier (if present), or the string representation of its integer numeric
value. [2]
* Calling Number() with an enum value as its first argument returns its
underlying number value.
* Calling String() with an enum value as its first argument returns the string
value for the enum member that defines the number (if present), or the string
representation of its integer numeric value. [2]
* Calling the valueOf() instance method on an enum value has the same effect as
Number() above.
* Calling the toString() instance method on an enum value has the same effect
as String() above. [2]
* Two enum members on the same enum or differing enum types with the same
underlying integer value are equivalent (==) but not strictly/reference
equivalent (===). [1]
I think these rules could satisfy both anyone who needs enum values to be
numeric (to support bitwise operations, bitmasks, ordinal indices, etc.) and
those that would like enum values to be unique in a fashion similar to using
Symbol().
[1] We have noticed in TypeScript some issues with Symbol-like equivalence with
enums if you have two versions of the same module in NodeJS due to specific
version dependencies, where you could have a.Color.Red !== b.Color.Red if *a*
and *b* are different versions of the same module. Generally I think having
enum values just really be numbers and not differing between == and === is less
of a footgun.
[2] If enum values of different types should be === to each other, you should
not be able to get a different result when you call .ToString(). In that case,
we could add a static `getName` method to the enum type to get the string value
for an enum.
Ron
> -----Original Message-----
> From: es-discuss [mailto:[email protected]] On Behalf Of
> Brendan Eich
> Sent: Wednesday, December 16, 2015 10:04 AM
> To: Alican Çubukçuoğlu <[email protected]>; es-
> [email protected]
> Subject: Re: Re: Propose simpler string constant
>
> On Wed, Dec 16, 2015 at 9:41 AM Alican Çubukçuoğlu
> <[email protected] <mailto:[email protected]> >
> wrote:
>
>
> How are enums declared?
> ```javascript
> let myVar = 13;
> enum myEnum = {Red, Green, Blue};
>
>
> No `=` between name and `{`.
>
> Enumerator scope is a good question. Clearly we do not want global scope.
> Rather, as a declaration immedicately contained by a block or top
> level, we want lexical scope for the enum name -- and (I think) for the
> enumerators'
> individual names.
>
> What about enumerator name scope for enum in class, without `static`?
> I'm not sure, but precedent says that the enumerator names define
> prototype properties.
>
> Expression as well as declaration `enum` form follows class and
> function precedent. Expression form requires a reserved identifier
> (not sym or sum or whatever), which `enum` has been forever, fortunately.
>
> I agree symbol values by default, with ` = 0` or some other number
> than
> 0 after the first enumerator name, looks confusing. Recall the first
> use-case in the o.p. was (implicit, should rather be explicit)
> reflection on the string value that spells the enumerator name.
>
> /be
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss