Reputation: 2598
I have a basic enum
below:
export enum Fruit {
apple,
banana
}
I want to export a fixed Array
of the enum
keys
export const Fruits = Object.entries(Fruit).map(f => f[0]);
Which should give, as desired, ['apple', 'banana']
and Fruits
typed as string[]
.
In an attempt for a more specific typing, I added as keyof typeof Fruit
like so
export const Fruits = Object.entries(Fruit).map(f => f[0] as keyof typeof Fruit);
Which gives me the type const Fruits: ("apple" | "banana")[]
. Is this the most that I can get?
I was aiming to get a typing like const Fruits: ["apple", "banana"] = ....
which I think is the perfect typing I should produce.
Footnote:
I wouldn't like to utilize the other method of defining enum
s, Just for the sake of avoiding that redundancy,
export enum Fruit {
apple = 'apple',
banana = 'banana'
}
And I am happy to do something like:
interface Meal {
fruit: keyof tpeof Fruit // since the default enum values are integers, use keys
}
So I'd be happy to have a solution that doesn't require me to do so. If there's no other way, do mention in your answer.
Upvotes: 1
Views: 804
Reputation: 33051
Please keep in mind that there is no guarantee that Object.entries
or Object.keys
preserve the order of keys. Hence you need to return a permutation of all possible states and not only ['apple', 'banana']
.
In this case it should be ['apple', 'banana'] | ['banana', 'apple']
.
export enum Fruit {
apple,
banana
}
// credits goes to https://twitter.com/WrocTypeScript/status/1306296710407352321
type TupleUnion<U extends PropertyKey, R extends any[] = []> = {
[S in U]: Exclude<U, S> extends never ? [...R, S] : TupleUnion<Exclude<U, S>, [...R, S]>;
}[U];
const keys = <
Keys extends string,
Obj extends Record<Keys, unknown>
>(obj: Obj) =>
Object.keys(Fruit) as TupleUnion<keyof Obj>;
const result = keys(Fruit)
// ["apple", "banana"] | ["banana", "apple"]
type Check = typeof result
Playground
I have used keys
instead of entries
because we are interested only in keys.
Here, in my blog, you can find more about convertiong union to tuples and function arguments inference
So, which enum is better: with values as integers or strings?
First of all, enums has their own flaws. COnsider this example:
export enum Fruit {
apple,
banana
}
const fruit = (enm: Fruit) => {}
fruit(100) // ok, no error
Is it safe - no!
Enums with integers are got to use if you have a bit mask.
It is better to use enum with string values:
export enum Fruit {
apple = 'apple',
banana = 'banana'
}
If you still want to use enum with integers, consider this example:
const enum Fruit {
apple,
banana,
}
const fruit = (enm: typeof Fruit) => { }
fruit(100) // expected error
Object.keys(Fruit) // impossible
If you want to use enum with integers and Object.keys/entries
you might want to use a safest approach:
export const Fruit = {
apple: 0,
banana: 1,
} as const
const fruit = (enm: typeof Fruit) => { }
fruit(100) // expected safe
Upvotes: 2