Enums 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 complicated 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 compiler 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