Get a list with all values from an Enum
alex321 opened this issue ยท 18 comments
Feature:
If we have an Enum, add the ability to access all of it's values as a list (or any other iterable).
enum Colour { red = 'RED', green = 'GREEN' }
Colour.toList() === ['RED', 'GREEN']
We could have a separate call for the keys? Or vice versa?
It is quite useful as you'd frequently want to be able to iterate over them somehow. Thanks!
Non-const
enums can be iterated over. With string enums this is easy because there is no reverse mapping from values to keys, so Object.keys(Colour).map(k => Colour[k as any])
is ['RED', 'GREEN']
.
A number enum will map from keys to values and from values to keys, so you must be careful:
enum E { A, B }
const keys = Object.keys(E).filter(k => typeof E[k as any] === "number"); // ["A", "B"]
const values = keys.map(k => E[k as any]); // [0, 1]
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
in case of string enum, simple Object.keys should do it
export enum ShipmentPickUpTime {
PUT_NONE = "PUT_NONE",
PUT_ANYTIME = "PUT_ANYTIME",
PUT_MORNING = "PUT_MORNING",
PUT_AFTERNOON = "PUT_AFTERNOON",
PUT_NIGHT = "PUT_NIGHT",
}
const keys = Object.keys(ShipmentPickUpTime);
console.log(keys);
// output
[ 'PUT_NONE',
'PUT_ANYTIME',
'PUT_MORNING',
'PUT_AFTERNOON',
'PUT_NIGHT' ]
Everything that is said above is accurate for enum
types. But what about const enum
types? It seems there is a design shortcoming here because there is no way to get a list of all values without changing const enum
to a plain old enum
. This is a problem because there are cases where a developer would want to use a const enum
but can not because they can not programatically get a list of all possible values.
In example suppose a developer wants to use a const enum
to represent a field in a mongoose schema. Here is an example of what this code might look like:
const enum Sex {
FEMALE = "Female",
MALE = "Male",
}
interface IUser {
firstName: string,
lastName: string,
sex: Sex,
}
If this developer were to try and create a Mongoose schema, they would not be able to enforce strong types on this sex field they would fail because they would not have access to all the possible const enum
values.
export const UserSchema: Schema = new Schema({
firstName: String,
lastName: String,
sex: {
enum: Object.values(Sex), // Won't Work because Sex is optimized out.
type: String,
},
}, {});
The only way for me to get this to work is to remove the const
declaration from the Sex enum
. That is not what I want to do because I have no intention of adding additional Sex values to that enum
. Thus this enum
should be able to be declared as a const enum
but can not be because of said aforementioned reasons. This seems like a bug or an oversight. What should be possible here is for me to call some special reserved property on the enum
that will return a list of all keys for that enum
. In example I would expect something like the following to work in the ideal scenario:
export const UserSchema: Schema = new Schema({
firstName: String,
lastName: String,
sex: {
enum: Sex._values,
type: String,
},
}, {});
This would allow me to use const enum
types and also would allow me to know all possible values of the enum
itself. Thoughts?
@stieg Maybe make a suggestion issue for that?
That is not what I want to do because I have no intention of adding additional Sex values to that enum.
This really isn't what const
is about. I recommend reading https://stackoverflow.com/questions/28818849/how-do-the-different-enum-variants-work-in-typescript
@andy-ms Yeah I can open a new issue if needed. Figured it would be better to first drop this here since this is related to the topic at hand.
@RyanCavanaugh Perhaps you could further elaborate on your point better since I don't seem to understand what point you are trying to make. I am aware of how Typescript handles const enum
objects by inlining them when it transpiles them; that is partially what my whole point is here. const enum
values can be inlined because there are not supposed to have any further additions. This enables TS to do the optimization here. In the case of normal enum
types, you have to use the lookup table because its possible to add other enum
later in the code. But what I feel you are missing is the meaning of the modifier const
. In pretty much all languages (including TS) its meaning is "does not change". And that is the primary intention and use of the const
modifier. The fact that you can apply optimizations as a result of its presence is a secondary benefit. I should be able to use a const enum
for the above code but because of how TS optimizes it I am unable to do so. This is why I feel its an oversight in the design of the const enum
. Effectively the TS transpiler should provide a way for me to get the values (or should disable the optimization so I can access the list of values). I would much prefer the former.
`export enum Status{
received = 'RECEIVED',
awaiting = 'AWAITING',
inProgress = 'IN PROGRESS'
}
Object.keys(Status).forEach(key => {
console.log(Interval[key]);
});
Output:>> RECEIVED
AWAITING
IN PROGRESS`
@Kevin2iBi
Everything was great, until some 'Interval' popped in ๐
Has this changed at all? I've tried doing what is posted above with no luck. Console logging shows it as undefined always.
export enum Names{
ownerName = 'John Smith',
clientName = 'Jane Doe',
workerName = 'James Bond'
}
const nameList = Object.keys(Names).forEach(key => {Names[key]});
console.log(nameList);
CONSOLE: undefined
@WholemealDrop you want map
not forEach
! You also need to return
from the function body. Probably best to go to StackOverflow
@RyanCavanaugh I see that now and it is working. Came here because this was the only spot I could find that discussed it. Thanks!
I've found Object.entries to work! Depending on your tsconfig, you'll need to add es2017.object.
// Add to tsconfig.json
"lib": ["es2017.object"],
Here's an example:
export enum REGIONS {
auckland = "Auckland";
waikato = "Waikato";
}
// no need to use map, if all you need are key-value tuples: [['auckland', 'Auckland'], ['waikato', 'Waikato']]
export const REGION_OPTIONS = Object.entries(REGIONS);
Seems the only way to get string enum values right now is as follows:
enum Foo {
A = "the letter A",
B = "the letter B"
}
fooKeys:string[] = Object.keys(Foo)
fooValues:Foo[] = fooKeys.map(k => Foo[k as any]).map(v => v as Foo)
This is really not ideal, I would expect TS to provide some syntactic sugar to shortcut the latter.
another workaround if you want just the enum strings (can easily be modified to get just the numbers too). Use Object.values
since that can return any data type, unlike Object.keys
which only returns strings.
enum JA_DICT_DATA {
'base',
'cc',
'check',
'tid',
'tid_map',
'tid_pos',
'unk_char',
'unk_compat',
'unk',
'unk_invoke',
'unk_map',
'unk_pos',
};
const JA_DICTS: JA_DICT_DATA[] = Object.values(JA_DICT_DATA).filter(x => typeof x === 'string');
You still can't get the type-level values of a string enum as far as i'm aware, for example....
export enum Status{
received = 'RECEIVED',
awaiting = 'AWAITING',
inProgress = 'IN PROGRESS'
}
declare const testKeys: keyof typeof Status // will give the Keys not the values (correct)
declare const testValues: Status[any] // will give back "string" not 'RECEIVED' | 'AWAIT......