// Because we cannot do "T extends enum" 😞 https://github.com/microsoft/TypeScript/issues/30611 export type EnumType = number | string; export type EnumVariable = { [key in T]: TEnumValue }; export interface IEnumParser { parseEnum(value: string, propertyName: string): TEnum; } export function createEnumParser( enumVariable: EnumVariable, ): IEnumParser { return { parseEnum: (value, propertyName) => parseEnumValue(value, propertyName, enumVariable), }; } function parseEnumValue( value: string, enumName: string, enumVariable: EnumVariable, ): TEnumValue { if (!value) { throw new Error(`missing ${enumName}`); } if (typeof value !== 'string') { throw new Error(`unexpected type of ${enumName}: "${typeof value}"`); } const casedValue = getEnumNames(enumVariable) .find((enumValue) => enumValue.toLowerCase() === value.toLowerCase()); if (!casedValue) { throw new Error(`unknown ${enumName}: "${value}"`); } return enumVariable[casedValue as keyof typeof enumVariable]; } export function getEnumNames ( enumVariable: EnumVariable, ): string[] { return Object .values(enumVariable) .filter((enumMember) => typeof enumMember === 'string') as string[]; } export function getEnumValues( enumVariable: EnumVariable, ): TEnumValue[] { return getEnumNames(enumVariable) .map((level) => enumVariable[level]) as TEnumValue[]; } export function assertInRange( value: TEnumValue, enumVariable: EnumVariable, ) { if (!(value in enumVariable)) { throw new RangeError(`enum value "${value}" is out of range`); } }