Russian translation of this article is available.
Here is a generic function that works with both numeric and string enums:
type EnumObject = {[key: string]: number | string};
type EnumObjectEnum<E extends EnumObject> = E extends {[key: string]: infer ET | string} ? ET : never;
function getEnumValues<E extends EnumObject>(enumObject: E): EnumObjectEnum<E>[] {
return Object.keys(enumObject)
.filter(key => Number.isNaN(Number(key)))
.map(key => enumObject[key] as EnumObjectEnum<E>);
}
Seems to be working in TypeScript 4 and 3.6+.
Usage
Numeric enum:
enum NumEnum {
A,
B,
C,
}
// The type is inferred as NumEnum[]
let numEnumValues = getEnumValues(NumEnum);
String enum:
enum StringEnum {
A = "a",
B = "b",
C = "c",
}
// The type is inferred as StringEnum[]
let stringEnumValues = getEnumValues(StringEnum);
Known issues
Unfortunately, this function doesn't type-check very well with mixed enums (containing both numeric and string values), at least at the time of TypeScript 4.6:
enum MixedEnum {
A = "a",
B = 2,
C = "c",
}
// The type is inconveniently inferred as MixedEnum.B[]
let mixedEnumValues = getEnumValues(MixedEnum);
// Declare type explicitly as a workaround:
let mixedEnumValues: MixedEnum[] = getEnumValues(MixedEnum);
let mixedEnumValues = getEnumValues(MixedEnum) as MixedEnum[];
Afterwords
Here's an amazing post by Zhenghao about type-level programming in TypeScript, if you want to tinker in this stuff too. It taught me a lot!
Zhenghao
Software Engineer
