You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The intent of string enums is that the values of the enum are opaque (i.e. they can be changed behind the scenes without breaking consumers). If you don't want that behavior, it's easy enough to use the pre-string-enum workarounds to create similar union types.
Numeric enums allow assignments from arbitrary numbers because we need to support bitflag enums and the | / & / etc operators return number. There's no corresponding behavior for string enums that needs to be supported.
Why don't | and & return enum types when their operands are enum types? I have no idea.
Even though string-based enums are now in TypeScript 2.4, it still is not possible to assign a literal string to an string-based enum (even though the value of the literal string matches a value of the enum). See the OPs example in TypeScript Playground.
It still fails on the following line:
const c2: Colors = "RED";
The example of OP is exactly the behavior I'd like to see: string literals and enum values being used interchangeably. It is however not yet possible.
Taking it a step further, the following should work as well:
enum ColorsEnum {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
type ColorsType = 'RED' | 'GREEN' | 'BLUE';
const c1: ColorsType = 'RED';
const c2: ColorsEnum = c1;
Should a new issue be created or can we reopen this one?
Bob van der Linden (@bobvanderlinden) this is the intended behavior, not a bug. String enum values are intentionally opaque to consumers; if you want a non-opaque string type, you should use a literal union (i.e. ColorsType).
Ryan Cavanaugh (@RyanCavanaugh) Thanks for your reply. I guess we'll have to discuss some more on swagger-codegen which of the two to use (enums or unions).
Ryan Cavanaugh (@RyanCavanaugh) IMO that's a limitation in the intended behavior that restricts the usage of string enums unnecessarily.
Consider how InversifyJS uses a literal union to specify settings. Because the existing behavior is to take in a string for some settings, the only way consumers can specify them in a type-safe manner is with string literals:
The entire point of string enums was to provide for a way to represent opaque string types because there's existing syntax to provide non-opaque string types.
If you don't want to use namespace, you can use const
constBindingScope: {Singleton: "Singleton"}=/* advantage: copy-paste from above 🙄 */{Singleton: "Singleton"}
The string enum in typescript differs from many other languages.
For example in Java you can write:
enum Colors {
RED("Red"), GREEN("Green"), BLUE("Blue")
}
and you can get the enum key by the value and reverse.
When having a java-server and a REST API (JSON) with a typescript written frontend this is quite a pain.
The java-server sends the KEY (RED) in the JSON response and in the gui we see the value "Red" because the enum is defined as enum Colors { RED = "Red"} in our typescript interface.
This is ok, but when trying to send the enum key to the server again in some sort of request it is quite a pain to send the Key RED that the java server recognises as Colors.RED. It does not recogise it as Colors.RED when sending "Red".
+1 for make the ability to reverse map from a value and get the key, something like Colors["Red] will get RED.
Ryan Cavanaugh (@RyanCavanaugh) Yes i know, I have done exactly that solution as you suggest, but I think this is a hack because a string enum does not work both ways which I think is strange behavior where many other langugaes works different.
I did it like this: string keyString: string = Object.keys(stringEnum).filter(key => stringEnum[key] === value)
(Does not have the code in front of me, but something like that)
Will return undefined if not found.
If the string enum worked both ways, you'd have no way to tell keys from values. Presumably you wantkeyFromValue("blue") to fail in the above example, right?
Activity
Knagis commentedon Aug 9, 2017
The value of the enum should
"RED", not"Red", the latter is the name of the value, not the value itself.kitsonk commentedon Aug 9, 2017
Kārlis Gaņģis (@Knagis) I think the OP is trying to point out that while the above doesn't work, the following does:
It is an incongruence in the language.
Knagis commentedon Aug 9, 2017
Kitson Kelly (@kitsonk) I mainly pointed out that OP was using the wrong constant. It still does not let assign even with the correct one though.
However in your example, unfortunately this also compiles:
const c3: Colors = 123;domoritz commentedon Aug 9, 2017
Thank you Kārlis Gaņģis (@Knagis). You're right that I used the wrong constant and I corrected my original post.
RyanCavanaugh commentedon Aug 9, 2017
The intent of string enums is that the values of the enum are opaque (i.e. they can be changed behind the scenes without breaking consumers). If you don't want that behavior, it's easy enough to use the pre-string-enum workarounds to create similar union types.
Numeric enums allow assignments from arbitrary
numbers because we need to support bitflag enums and the|/&/ etc operators returnnumber. There's no corresponding behavior for string enums that needs to be supported.Why don't
|and&return enum types when their operands are enum types? I have no idea.domoritz commentedon Aug 9, 2017
Thanks for the explanation Ryan Cavanaugh (@RyanCavanaugh)! Can you point me to some documentation for the best practice workarounds from pre-string-enums? I'm currently using namespaces (https://github.com/vega/vega-lite/blob/6ea812df890070f4cc7f72ac3efc96eaa44a8c4e/src/type.ts#L4) because Daniel Rosenwasser (@DanielRosenwasser) suggested it to me last year. However, with this suggestion I have a namespace and a type with the same name if I want to use string literals and constants at the same time.
RyanCavanaugh commentedon Aug 9, 2017
I recommend this approach: #3192 (comment)
bobvanderlinden commentedon Oct 19, 2017
Even though string-based enums are now in TypeScript 2.4, it still is not possible to assign a literal string to an string-based enum (even though the value of the literal string matches a value of the enum). See the OPs example in TypeScript Playground.
It still fails on the following line:
The example of OP is exactly the behavior I'd like to see: string literals and enum values being used interchangeably. It is however not yet possible.
Taking it a step further, the following should work as well:
Should a new issue be created or can we reopen this one?
RyanCavanaugh commentedon Oct 19, 2017
Bob van der Linden (@bobvanderlinden) this is the intended behavior, not a bug. String enum values are intentionally opaque to consumers; if you want a non-opaque string type, you should use a literal union (i.e.
ColorsType).bobvanderlinden commentedon Oct 22, 2017
Ryan Cavanaugh (@RyanCavanaugh) Thanks for your reply. I guess we'll have to discuss some more on swagger-codegen which of the two to use (enums or unions).
JoshuaKGoldberg commentedon Dec 11, 2017
Ryan Cavanaugh (@RyanCavanaugh) IMO that's a limitation in the intended behavior that restricts the usage of string enums unnecessarily.
Consider how InversifyJS uses a literal union to specify settings. Because the existing behavior is to take in a string for some settings, the only way consumers can specify them in a type-safe manner is with string literals:
...but that means we can't use slightly-easier-to-get-intellisense-for enum members:
Does that change your opinion?
Relevant issue: inversify/InversifyJS#706
RyanCavanaugh commentedon Dec 11, 2017
There's already a syntax available to you for that: exported consts in namespaces
JoshuaKGoldberg commentedon Dec 11, 2017
That does work:
...but it's ugly and uses the discouraged
namespacesyntax. I suppose we can use it... but it would be cleaner just to allow this in enums 😦.RyanCavanaugh commentedon Dec 11, 2017
The entire point of string enums was to provide for a way to represent opaque string types because there's existing syntax to provide non-opaque string types.
If you don't want to use
namespace, you can useconstwiddde commentedon Dec 13, 2017
The string enum in typescript differs from many other languages.
For example in Java you can write:
enum Colors {
RED("Red"), GREEN("Green"), BLUE("Blue")
}
and you can get the enum key by the value and reverse.
When having a java-server and a REST API (JSON) with a typescript written frontend this is quite a pain.
The java-server sends the KEY (RED) in the JSON response and in the gui we see the value "Red" because the enum is defined as enum Colors { RED = "Red"} in our typescript interface.
This is ok, but when trying to send the enum key to the server again in some sort of request it is quite a pain to send the Key RED that the java server recognises as Colors.RED. It does not recogise it as Colors.RED when sending "Red".
+1 for make the ability to reverse map from a value and get the key, something like Colors["Red] will get RED.
RyanCavanaugh commentedon Dec 13, 2017
widdde commentedon Dec 13, 2017
Ryan Cavanaugh (@RyanCavanaugh) Yes i know, I have done exactly that solution as you suggest, but I think this is a hack because a string enum does not work both ways which I think is strange behavior where many other langugaes works different.
I did it like this:
string keyString: string = Object.keys(stringEnum).filter(key => stringEnum[key] === value)(Does not have the code in front of me, but something like that)
Will return undefined if not found.
RyanCavanaugh commentedon Dec 13, 2017
If the string enum worked both ways, you'd have no way to tell keys from values. Presumably you want
keyFromValue("blue")to fail in the above example, right?widdde commentedon Dec 13, 2017
Some kind of valueOf is what I'm seeking for
Colors.valueOf("Red") will return RED for example.
Native, not self written functions. ;)
m1gu3l commentedon Jan 29, 2018
There's always this pattern:
However it would be useful if we had a modifier to allow permissive string enum types.