I have a typescript enum:
export enum LanguagesEnglish {
'enUS'= 'English',
'zhHant'= 'Mandarin, Traditional',
};
I would like to use it to generate options for a selector component. To do so, I think I should be able to map over the keys of the enum to generate objects in the shape the selector component library requires, defined as:
type pickerOption = {
code: string,
label: string,
};
I try to define that type as
type pickerOption = {
code: keyof LanguagesEnglish,
label: LanguagesEnglish,
};
Now, I’d like to generate objects of this type and put them in an array.
function generateOption(key: keyof LanguagesEnglish): pickerOption {
return {
code: key,
label: LanguagesEnglish[key],
}
}
Now to map over the enum and generate this array.
const options = Object.keys(LanguagesEnglish).map(generateOption);
I’ve been plagued with typescript errors that I find quite confusing. For example, in my generateOption function, the label property has an error.
Element implicitly has an 'any' type because expression of type 'number | unique symbol | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | ... 35 more ... | "at"' can't be used to index type 'typeof LanguagesEnglish'.
No index signature with a parameter of type 'number' was found on type 'typeof LanguagesEnglish'. (lsp)
This doesn’t make sense to me, because I explicitly indicated that key is not a number | unique symbol | ..., I indicated that it’s a keyof LanguagesEnglish in my function definition.
What’s the correct Typescript way to manage mapping back and forth between Enum types?
>Solution :
Enums are not what you expect them to be. In fact, in runtime, the enums are compilers to an object with the key: value and also value: key. Due to this and to other points, keyof SomeEnum is not what you want:
// number | typeof Symbol.iterator | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | ... 36 more ... | "valueOf"
type Case1 = keyof LanguagesEnglish;
What you should actually do is to get the keys of the type of the enum:
// type Case2 = "enUS" | "zhHant"
type Case2 = keyof typeof LanguagesEnglish;
Now, let’s update your types:
type pickerOption = {
code: keyof typeof LanguagesEnglish;
label: LanguagesEnglish;
};
function generateOption(key: keyof typeof LanguagesEnglish): pickerOption {
return {
code: key,
label: LanguagesEnglish[key],
};
}
Another issue is that Object.keys() don’t return the exact keys, but string[], which is described in this question.
So, unfortunately, we will need to use assertion to make the compiler happy:
const options = (
Object.keys(LanguagesEnglish) as (keyof typeof LanguagesEnglish)[]
).map(generateOption);