-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
String valued members in enums #15486
Conversation
A = 123, | ||
} | ||
declare enum E03 { | ||
A = hello, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think is supposed to be the string "hello"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. Now fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@basarat @ahejlsberg this might be more appropriate https://twitter.com/slyphon/status/852307969219977216
@mhegazy You want to take a look before I merge this? |
@aaronbeall
So the following code:
would be the same as:
|
@ahejlsberg @wizarrc To me, a generic Enum is more useful than a mixed Enum. I would rather have |
@robertpenner I agree, and it should throw a lint warning if types are mixed (by setting an initializer) with generic enums under strict mode. As for specifying any type, that would be difficult to implement as property assessors only support string, number (positive integer only), and I think symbols. But who is to say that that wont increase over time, so by making it generic, it would make it more future proof, and add constraints to value T in your example in the d.ts file that defines the generic enum. I'm not sure disallowing mixed enums altogether is the right thing to do. I think the current PR is a good start, and should add generic enums later (future feature request???). |
Personally I don't see much value in enums of mixed type, either (I can't remember ever doing that). I like the generic idea but I don't know how it would fill in values for anything other than |
@aaronbeall unless the |
Great feature, would there be any possibility of implementing nested enums? Like instead of doing this: const enum EnumDefinition {
name = "X"
}
const enum EnumDefinitionGroup1 {
A = "B1"
}
const enum EnumDefinitionGroup2 {
A = "B2"
} to do this: const enum EnumDefinition {
name = "X",
Group1 {
A = "B1"
},
Group2 {
A = "B2"
}
} one can cheat a bit using periods const enum EnumDefinition {
name = "X",
"Group1.A" = "B1",
"Group2.A" = "B2"
} but it's not really an optimal solution since one would have to access everything with One can use static class members to achieve the same structure; class EnumDefinition {
static readonly __name = "X";
static readonly Group1 = {
A: "B1"
};
static readonly Group2 = {
A: "B2"
};
} Of course one can't use the key "name" because it conflicts with the built in name nor does anything get inlined. Regardless I'm very happy this has been merged. |
@Draqir I'm lost by your example. Is EnumDefinitionGroup1 the same as Group1? How do the groups relate to each other? Also, what value do nested enums provide? |
@wizarrc Benefit: avoid magical strings. The more times you need to repeat a thing, the higher the risks are that there'll be bugs, and misspelling a string is quite easy. So in order to avoid that I usually use "blueprints" like the following: class TvChannel {
static readonly Table = "TvChannelTable";
static readonly RelatedChannels = {
Category: "Category",
};
static readonly ChannelProperties = {
Audience: "Audience"
};
}
class Channels {
static readonly Sport = "Sport";
} So if I wanted to retrieve all db(TvChannel.Table).conditions(TvChannel.RelatedChannels.Category, Channels.Sport) However this could just be nicely inlined by the TypeScript compiler to db("TvChannelTable").conditions("Category", "Sport") and the whole class wouldn't even need to exist.. Currently I can achieve the same with either using wonky periods for groups or using very many enums.. Neither is really perfect but it's still a huge improvement |
@Draqir so it's like enum namespaces? If that's the case, I think that's an awesome idea! |
@wizarrc Actually I hadn't considered that, but yes, it's exactly as enum namespaces. |
@Draqir One more thought. Support enum flags with namespaces, where each namespace (nested group) is a compiler enforced mutually exclusive flag, but it all is represented as a single number. I'm not sure how well the compiler can enforce or if it's even possible because it's collapsed to a single type but I've seen several projects (i.e. angular2+) use enum flags to combine types (groups or namespaces) in their low level implementation for performance reasons. It would be nice to have more static checking on mutual exclusion to make sure both items are not selected inside the same group. Take a look at the Maybe if namespaces would have a combined type for number enums, like |
enums and namespaces already merge. enum EnumDefinition {
name = "X",
}
namespace EnumDefinition {
export enum Group1 {
A = "B1"
}
export enum Group2 {
A = "B2"
}
} |
@mhegazy @Draqir Oops. It did not occur to me that merging namespace and enum is basically enum groups. Sounds like a win. Unfortunately I don't think there is a way to collapse that all into a single number enum like my example shown above from the Angular project. That would be a nice way to have great abstraction and still low level performance. Something that can only be done if it is a pure number enum. |
It's very close but no cigar. error TS2339: Property 'name' does not exist on type 'typeof EnumDefinition'. (Todays tsc version) and I can't use const on the first enum const enum EnumDefinition {
name = "X",
}
namespace EnumDefinition {
export const enum Group1 {
A = "B1"
}
export const enum Group2 {
A = "B2"
}
} The things inside the namespace works good though |
const enums are removed, so they can not merge with namespaces. so this only works with none-const enums. |
@mhegazy If they allowed nesting namespaces inside of enums, they should be able to allow const since nothing is being merged and everything is known upfront. This external merging is the problem. Imagine this code:
Then you could access the enum by
where |
+1 for @aaronbeall's comment on something along the lines of |
There should be a mistake in TS playground. When I type enum Mixed {
C = "hi",
F = "bye"
} According to this change, it should be var Mixed;
(function (Mixed) {
Mixed[Mixed["C"] = "hi"] = "C";
Mixed[Mixed["F"] = "bye"] = "F";
})(Mixed || (Mixed = {})); |
Enum types in TypeScript come in two flavors: Numeric enum types and literal enum types. An enum type where each member has no initializer or an initializer that specififes a numeric literal, a string literal, or a single identifier naming another member in the enum type is classified as a literal enum type. An enum type that doesn't adhere to this pattern (e.g. because it has computed member values) is classified as a numeric enum type.
With this PR we implement the ability for literal enum types to have string valued members.
The above declaration creates an enum object
Color
with three membersRed
,Green
, andBlue
. The members have corresponding new string literal enum types,Color.Red
,Color.Green
, andColor.Blue
that are subtypes of the string literal types"red"
,"green"
, and"blue"
respectively. Finally, the declaration introduces a typeColor
that is an alias for the union typeColor.Red | Color.Green | Color.Blue
.When a string literal enum type is inferred for a mutable location, it is widened to its corresponding literal enum type (rather than being widened to type
string
).The above behavior exactly corresponds to the behavior for numeric literal enum types, only the values are strings instead of numbers. In fact, an enum literal type can contain any mix of numeric literal values and string literal values.
The
Mixed
type above is an alias forMixed.A | Mixed.B | Mixed.C | Mixed.D | Mixed.E | Mixed.F
, which is a subtype of0 | 1 | "hi" | 10 | 11 | "bye"
.Enum members with string literal values must always specify an initializer, but enum members with numeric literal values may omit the initializer provided it is the first member or the preceding member has a numeric value.
In the code emitted for an enum declaration, string valued members have no reverse mapping. For example, the following code is emitted for the
Mixed
enum declaration above:As is already the case, no code is generated for a
const enum
. Instead, the literal values of the enum members are inlined where they're referenced.Fixes #1206.
Fixes #3192.