Although not necessary, enum keys are usually declared all in CAPS. However, the enum value can be different from the key itself.
declare enum MyEnum {
PRODUCTION = "production",
DEVELOPMENT = "development",
SANDBOX = "sandbox"
}
If I were to use an external string value matching my enum value to index my enum, It will not work as the key is not the same as the value:
const lowercase = "production";
const UPPERCASE = "PRODUCTION";
const bad = MyEnum[lowercase]; // does not work
const good = MyEnum[UPPERCASE]; // works
I want to use the enum value as a way to index the enum. Am I forced to always match identically the enum value and enum key to be able to index the enum dynamically, or do I have to use some string value manipulation to ensure the key matches the value?
Although not necessary, enum keys are usually declared all in CAPS. However, the enum value can be different from the key itself.
declare enum MyEnum {
PRODUCTION = "production",
DEVELOPMENT = "development",
SANDBOX = "sandbox"
}
If I were to use an external string value matching my enum value to index my enum, It will not work as the key is not the same as the value:
const lowercase = "production";
const UPPERCASE = "PRODUCTION";
const bad = MyEnum[lowercase]; // does not work
const good = MyEnum[UPPERCASE]; // works
I want to use the enum value as a way to index the enum. Am I forced to always match identically the enum value and enum key to be able to index the enum dynamically, or do I have to use some string value manipulation to ensure the key matches the value?
Share Improve this question asked Sep 4, 2022 at 17:49 bombillazobombillazo 3102 gold badges7 silver badges15 bronze badges 8- 1 Does this answer your question? How can I cast a string to an enum in Typescript – Blackhole Commented Sep 4, 2022 at 18:15
-
Why are you using enums here at all? What is the intent of writing
MyEnum["production"]
if the goal is to get"production"
? Why not just use"production"
itself rather than passing it through another object? I am failing to understand the use case. – jcalz Commented Sep 4, 2022 at 18:25 -
Like, let's remove
enum
from the situation entirely by replacing it with aconst
-asserted object as shown here. You have the same issue, where you can't just index into it with something that isn't a key. Do you agree that this is the same issue? If so, then we can dispense withenum
and just talk about how to index into any object with a case-insensitive key – jcalz Commented Sep 4, 2022 at 18:28 - If so, then you should just convert to uppercase before indexing, possibly with a helper function to tell the piler that the literal type should also be uppercase, like this. If you need an enum then like this. Does this fully address your question? If so I can write up an answer explaining. If not, what am I missing? (Please @jcalz mention me if you reply, or I won't be notified) – jcalz Commented Sep 4, 2022 at 18:31
- @Blackhole hey, not really. I am not trying to cast the values to an enum. – bombillazo Commented Sep 5, 2022 at 4:24
2 Answers
Reset to default 5Enums in TypeScript emit as plain JavaScript objects. Thus to some extent this question is really about how you can access JavaScript objects with case-insensitive keys. The short answer is that you can't and you need to work around it. The longer answer is that you could probably build a Proxy
for your object that behaves the way you want, but it would be a lot more plicated than just working around it, especially anything that would convince TypeScript that this is what you're doing.
If you know that the object keys are all uppercased then the easiest workaround is to just uppercase the candidate key yourself before indexing into the object. TypeScript doesn't understand what the toUpperCase()
method of string
s does to string literal types. For example, if you have a value v
of type "foo"
, the piler won't understand that v.toUpperCase()
is a value of type "FOO"
:
let x: "FOO" = "foo".toUpperCase(); // error!
but you could write a wrapper for the toUpperCase()
method that behaves this way:
const uc = <T extends string>(x: T) => x.toUpperCase() as Uppercase<T>;
let x: "FOO" = uc("foo"); // okay
Here uc
is using the Uppercase<T>
intrinsic type in its return type.
So the workaround here is to just pass keys through uc()
before indexing:
const UPPERCASE = "PRODUCTION";
const good = MyEnum[uc(UPPERCASE)]; // okay
// const good: MyEnum.PRODUCTION
const lowercase = "production";
const stillGood = MyEnum[uc(lowercase)]; // okay
// const stillGood: MyEnum.PRODUCTION
If you don't know that the keys are all uppercase then you need to search your object for an appropriate key. You can write a helper function that does this, and even give it a call signature that TypeScript can use:
function idxIgnoreCase<K extends string, T extends object>(
obj: T,
k: K extends (
Uppercase<K> extends Uppercase<Extract<keyof T, string>> ? unknown : never
) ? K : Extract<keyof T, string>
): { [P in Extract<keyof T, string>]-?:
Uppercase<P> extends Uppercase<K> ? T[P] : never
}[Extract<keyof T, string>] {
return Object.entries(obj).find(
([p, v]) => k.toUpperCase() === p.toUpperCase())?.[1] as any;
}
That's ugly, but it works:
const ret = idxIgnoreCase(MyEnum, "dEvElOpMeNt");
// const ret: MyEnum.DEVELOPMENT
console.log(ret) // "development"
I'm not even going to try to write the helper function as a Proxy
, since doing so would require telling TypeScript that the resulting object has hundreds of keys, one for each possible case for the original keys. And all for the dubious advantage of writing MyProxyEnum.dEvElOpMeNt
instead of f(MyEnum, "dEvElOpMeNt")
. Especially since other TypeScript and JavaScript developers looking at the code might be very confused, since they'd be expecting case sensitive keys.
Again, the workaround of modifying the key before indexing is by far the easiest and most understandable approach.
Playground link to code
This is enum naming convention:
Use uppercase letters for your enums - they are global constants which have usable values. They are more than just types, which use usually like Foo or TFoo.
The keys on an enum tend to be titlecase.
However, in this case, there is no need to use Enums