-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Allow enums of types other than number #1206
Comments
From the original proposal As in the title, and as discussed extensively here, it would be very helpful to allow enums of types other than number. At the very least, if allowing arbitrary types is too much work, string enums should be allowed. The current codegen for enums actually works with strings as-is, the compiler just flags errors. Consider:
As of 0.9, this gets to compiled to:
which is 100% functioning JavaScript that works as you expect it to. In addition, the whole concept of "overloads on constants" would be a lot cleaner with a string-based enum:
Closed Jul 28 at 5:18 PM by jonturner
|
Changed the one example from the original for the Document interface. It seems that you would only specify the name of the enum.-- Values passed in would become bounded to the use of the enum constants (and possibly to free-form string literals, where their values can be statically determined to be within the enum's domain values) |
@basarat That seems interesting from a theoretical standpoint, and I can definitely see uses for it. It would solve my case.--At least giving some level of intellisense and compile-time validation. I personally believe accessing enum constant fields makes for a much more natural experience for non-functional languages (and at least has parity with enums, which is what they they really are. I really don't think we should create a new concept for a construct that already exists. That will be confusing to too many people). |
I would love this when I deal with C++ enums compiled by Emscripten. // C++
enum Month {
Jan, Feb, Mar
}; // I can do this, but they really are not numbers!
declare enum Month {
Jan, Feb, Mar
}
interface EmscriptenEnum {
value: number; /* some more properties ... */
}
interface Month extends EmscritenEnum {
}
declare module Month {
var Jan: Month;
var Feb: Month;
var Mar: Month;
}
// Month.Jan.value == 0, Month.Feb.value == 1, ... |
C#/C++ lets you define base type of enum, although restricted to numeric type. I would like to be able to do the same but generalized to arbitrary type. It should work with builtin types like string, so: enum Foo extends string {
BAR,
BAZ = "surprise"
} compiles to: var Foo;
(function (Foo) {
Foo["BAR"] = "BAR";
Foo[Foo["BAZ"] = "surprise"] = "BAZ";
})(Foo || (Foo = {})); but also user types, to handle enum object pattern that is used by enums in Java and common in other languages (as requested above): interface IFoo {
id: string;
code: number;
}
enum Foo extends IFoo {
BAR = { id: "BAR", code: 123 },
BAZ = { id: "", code: 0 }
} compiles to: var Foo;
(function (Foo) {
Foo["BAR"] = { id: "BAR", code: 123 };
Foo["BAZ"] = { id: "", code: 0 };
})(Foo || (Foo = {})); Perhaps with a convention that if interface has fields |
How would this work? enum Test {
Foo = "Bar",
Bar = "Baz",
Baz = "Foo"
} |
Either compilation error or omit generation of reverse mapping for Bar. I'm not sure which one I prefer. |
In fact I'd prefer if TypeScript didn't generate reverse mapping in the same object. Perhaps all reverse mappings (for number based enums too) should go inside some property, say |
In my mind these really need to compile down to plain strings. String enums If it isn't possible to define strongly typed interfaces for these cases,
|
I think the first thing should be strings const enum. const enum A {
"x",
"y",
"z"
}
var a : A;
a = // Intellisense suggest "x", "y" or "z"
a = "x"; // OK
a = "b"l // Error |
My concern with the const enum approach is that it will lead to cases where the value can not be determined to be conforming when being passed into an API. (would this be a warning?, what happens if you get that warning? do you have to add casts all over the place to mitigate the warning?). If you treat them as something that can never be converted to or from a string, they will do exactly what they need to do.-- It should work exactly like the integer enums, with the exception that the values are strings. I think it would be fine if the keys and values are locked together, meaning that
Could be fine, but any string assignments without casts should fail.
In cases where they are converted from a string, a cast should be used.-- But even then, behavior like that can indicate that an input did not come from a proper source. Additionally, this will help tooling like the the schema validation/generators to appropriately generate the appropriate validation code.-- Schema validation is going to become even more important in times to come.--One of my big concerns with javascript is general is the lack of validation.-- Now that people are running servers on this stuff. As far as I can tell, the bulk of the work needed to get this done is removing one validation/compiler error against enum definitions (assigning types other than number).-- There might be some smarts to make sure people don't directly assign numbers to enum, but they shouldn't be doing that without a cast anyway either... |
How about using generics for enum? enum<string> Foo1 {
BAR, // default value is "BAR".
BAZ = "x" // also you can specify the vaule.
}
var e: Foo1 = Foo1.BAR; // ok
var s: string = Foo1.BAR; // ok
var n: number = Foo1.BAR; // error
enum<boolean> Foo2 {
BAR = true, // assigning is required. only number and string enum have default value.
BAZ = false
} You can use every type for enum like |
Base type syntax makes more sense IMHO and is already familiar to C# developers. |
+1 for the generics suggestion. |
and Closure Compiler uses /**
* @enum {string}
*/
var Foo = {
BAR: 'BAR',
BAZ: 'BAZ'
}; Also default type of enum in Closure is number. It's in common with TypeScript's case.
|
+1 for the generics syntax. I just need some sort of functionality. Here's my use case: I'm wanting to convert this bit of ES6 + Lodash into something I can more conveniently statically check. (Note: the boilerplate is because I use it for other things as well, things that would have to be generated at build time to statically type-check.) // Enum
const tokenTypes = makeEnumType(([value, length]) => ({value, length}));
export const Tokens = tokenTypes({
OpenCurved: ['(', 1],
CloseCurved: [')', 1],
OpenBracket: ['[', 1],
CloseBracket: [']', 1],
OpenCurly: ['{', 1],
CloseCurly: ['}', 1],
Identifier: ['Identifier', 0],
String: ['String', 0],
EOF: ['EOF', 0],
});
// Utilities
function deepFreeze(obj) {
Object.freeze(obj);
_.forOwn(obj, value => typeof value === 'object' && deepFreeze(obj));
return obj;
}
_.mixin({
removeProto(obj) {
let ret = Object.create(null);
_.forOwn(obj, _.partial(_.assign, ret));
return ret;
},
freeze: deepFreeze,
});
function mapObject(obj, f) {
let ret = Object.create(Object.getPrototypeOf(obj));
forOwn(obj, (value, i) => ret[i] = f.call(obj, value, i, obj));
return ret;
}
function makeEnumType(transformer) {
return obj => _.chain(mapObject(obj, transformer))
.removeProto()
.freeze()
.value();
} The best I can currently do in the first case is this (the second is similar): interface TokenType {
type: string;
value: string;
}
function type(value: string, length: number): TokenType {
return {value, length};
}
class TokenTypes {
// This shouldn't be called as a constructor...but it still type-checks.
static OpenCurved: TokenType = type('(', 1);
static CloseCurved: TokenType = type(')', 1);
static OpenBracket: TokenType = type('[', 1);
static CloseBracket: TokenType = type(']', 1);
static OpenCurly: TokenType = type('{', 1);
static CloseCurly: TokenType = type('}', 1);
static Identifier: TokenType = type('Identifier', 0);
static String: TokenType = type('String', 0);
static EOF: TokenType = type('EOF', 0);
} With the above syntax, I could use the following: enum<TokenType> TokenTypes {
OpenCurved = type('(', 1),
CloseCurved = type(')', 1),
OpenBracket = type('[', 1),
CloseBracket = type(']', 1),
OpenCurly = type('{', 1),
CloseCurly = type('}', 1),
Identifier = type('Identifier', 0),
String = type('String', 0),
EOF = type('EOF', 0),
} This is where enums can really help. (The boilerplate is mainly for places where a preprocessor would be helpful.) |
@jbondc I like it. You've picked the simple, obvious and intuitive solution, that can enforce type safety in many of the situations that some of the above options can't without a full static analysis of a program.-- It additionally would support creating straight forward domain bound json schma validations right from the typescript definition. Any interest in #2491 ? |
+1 for this and I am suggesting supporting all primitive types for enum values. Just like Swift. This is the syntax in Swift which I really like: enum Audience: String {
case Public = "Public"
case Friends = "Friends"
case Private = "Private"
} |
I would like to mention that, because of ECMAScript language limitations I do feel this would be incredibly useful, though. On Tue, Jun 16, 2015, 16:41 Mohsen Azimi [email protected] wrote:
|
+1 for the generics syntax. enum myStringEnum < string > {
one = "myOneValue",
two = "myTwoValue",
three = "myThreeValue"
}
var value = myStringEnum.one; //emits 'var value = "myOneValue" /* myStringValue.one */' emit inline value for primitive types (number, string, boolean ...) is very useful because the enum declaration does not need to be emitted in javascript but is used only to enforce compile-type validation |
@Gambero81 are you aware of const enums for the purposing of skipping emit like you want? |
@danquirk const enum are great, but attually does not support generics type but is only for numeric type.. |
Also const enums should be restricted to primitive types, and the whole reason they aren't emitted is to lessen code size (especially minified), which would not usually be the case with other types, such as strings and booleans. But as for normal enums of non-numeric types, this definitely should be possible. Currently, there is little reason to prefer a normal enum over a const enum, and this would wonderfully fix that. I do have (another) possible syntax, in case you all might like it: function type(ch: string, length: num): TokenType {
// code...
}
enum TokenTypes: TokenType {
OpenCurved = type('(', 1),
CloseCurved = type(')', 1),
OpenBracket = type('[', 1),
CloseBracket = type(']', 1),
OpenCurly = type('{', 1),
CloseCurly = type('}', 1),
Identifier = type('Identifier', 0),
String = type('String', 0),
EOF = type('EOF', 0),
} |
Coming from the Java world I really miss the ability to define methods on my enums. Would really like for enums to be full class citizens but still able to be used in switch statements (with auto complete support) Allows for things like: var planet = Planet.fromOr(myForm.planetSelect.selectedIndex,Planet.Earth)
myForm.swallowEnabled.checked=Planet.Earth.canSwallow(planet); Example enum: enum Planet {
private label:string; <--custom property
private size:string; <--custom property
private orbit:number; <--custom property
Mercury("Mercury",1,1),Venus("Venus",2.8,2),Earth("Home,3,3)...; <--declare 'constants'
Planet(label,size,orbit){ <--private constructor
.....
}
//a custom instance method
public canSwallow(planet:Planet):bool { //<--custom method
return planet.size < this.size;
}
public isCloserToSun(planet:Planet):bool { //<--custom method
return planet.orbit < this.orbit;
}
//all functions below auto generated, or implemented in base enum. Shown here in semi typescript
//convert from string, number or type or use given default
public static fromOr(nameindexOrType:string,defVal:Planet=null):Planet { //<--auto generated
var e = from(nameindexOrType);
return e==null?defVal:e;
}
//convert from string, number or type or return null
public static from(nameindexOrType:string):Planet { //<--auto generated
if(nameindexOrType == null){ return null; }
if(typeof(nameindexOrType) =='Number'){
switch(nameindexOrType){
case 0:return Planet.Mercury;
case 1:return Planet.Venus;
...
}
}if(typeof(nameindexOrType) =='String'){
nameindexOrType = nameindexOrType.ToUpperCase();
switch(nameindexOrType){
case 'MECURY':return Planet.Mercury;
...
}
}
return null;
}
public static get names():string[] { //<--auto generated
return ['Mercury','Venus','Earth',...];
}
public static get values():Planet[] { //<--auto generated
return [Planet.Mercury,Planet.Venus,Planet.Earth',...];
}
}
} internally there would also be a field called 'index' and 'name' which are used for comparison checks (or just one of them) If no custom properties or methods, then everything compiled down to a number or a string only. |
@bertvanbrakel I like your idea, but I think that may fit better as another bug, and also, this may fall under "Not yet". Another thing, IMO, there aren't a lot of use cases for having methods on enums, anyways, since they can easily become too coupled to the enum, and hard to generalize. |
So far, here's the possible syntaxes I've found here... // 1.
enum Enum extends string {
Foo, // "Foo"
Bar = "something",
}
// 2.
enum<string> Enum {
Foo, // "Foo"
Bar = "something",
}
// 3.
enum Enum: string {
Foo, // "Foo"
Bar = "something",
} WDYT? |
Some discussion today. Unorganized notes; refer to #1206 (comment) for a mini-spec with some changes as follows
|
Would this restrict enums to just primitives (or even just |
I don't think enums with values other than strings or numbers are on the table at this point. It's unclear what a |
Makes sense
Pff, I swear there are totally legit reasons for enum bool: boolean = {
true = false,
false = true,
}; |
Actually you can achieve that with enum bool {
true = 0,
false = 1
} |
Hah
…On Tue, Apr 4, 2017 at 3:05 PM Daniel Busłowicz ***@***.***> wrote:
Actually you can achieve that with
enum bool {
true = 0,
false = 1
}
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#1206 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAChndeXT4OF-SiF0Vd16Au4nZcYIGh9ks5rsr6fgaJpZM4C9e4r>
.
|
@RyanCavanaugh Is it possible to allow reference type enums provided they aren't |
@nevir Don't forget FileNotFound. |
@isiahmeadows it's possible, but it'd have to be well-justified because it's a lot more complexity. For a |
@RyanCavanaugh Oh, I see now, and it's not really a short term need for me. Maybe, in the future, could nominal subtyping could help? |
In my scenario I needed sort of custom object enum, since my class does not have any method that would make the inheritance necessary, I just used this class with static props: export class ViewerItemCardType {
public static Big: ViewerItemCardType = new ViewerItemCardType(1, "FeaturedBig", 330, 660);
public static Medium: ViewerItemCardType = new ViewerItemCardType(2, "FeaturedSmall", 155, 310);
public static Small: ViewerItemCardType = new ViewerItemCardType(3, "NormalArticle", 100, 200);
private constructor(
public id: number,
public name: string,
public imageHeight: number,
public imageWidth: number
) { };
} I can access to these "complex" enums like:
@isiahmeadows , Does that particular scenario match with your definition at some point ? |
@jquintozamora that's awesome! I think you can infer the extra type-hints though, and you'd likely want to define a means of enumerating the options as well, depending on your use-case - so like:
|
@jquintozamora also note that it's a closed set though - you can't use declaration merging to add new values, so that's another thing we'd (hopefully) get from real typed enums. |
Hi @mindplay-dk , |
Implementation now available in #15486. |
I released ts-enums as a library that enables creating full-class, Java-style enums. Maybe it can be useful for some people on this thread. Suggestions for improvements are welcome :) |
With Angular 2
// angular 2 Component in type script
// in your html file
|
I'm reopening this issue, because it was closed with the move from codeplex, and doesn't seem to have been re-opened. https://typescript.codeplex.com/workitem/1217
I feel like this is very important for a scripting language.-- Especially for objects that are passed back and forth over web service calls.-- People generally don't use integer based enums in json objects that are sent over the wire, which limits the usefulness of the current enum implementation quite a bit.
I would like to re-propose the original contributor's suggestions verbatim.
The text was updated successfully, but these errors were encountered: