export type EnumType = number | string ; export type EnumVariable < T extends EnumType , TEnumValue extends EnumType > = { [ key in T ] : TEnumValue ; } ; export interface IEnumParser < TEnum > { parseEnum ( value , propertyName ) : TEnum ; } export function createEnumParser < T extends EnumType , TEnumValue extends EnumType > ( enumVariable ) { return { parseEnum : ( value , propertyName ) => parseEnumValue ( value , propertyName , enumVariable ) , } ; } function parseEnumValue < T extends EnumType , TEnumValue extends EnumType > ( value , enumName , enumVariable ) { if ( ! value ) { throw new Error ( `undefined ${ 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 < T extends EnumType , TEnumValue extends EnumType > ( enumVariable ) { return Object . values ( enumVariable ) . filter ( ( enumMember ) => typeof enumMember === 'string' ) as string [ ] ; } export function getEnumValues < T extends EnumType , TEnumValue extends EnumType > ( enumVariable ) { return getEnumNames ( enumVariable ) . map ( ( level ) => enumVariable [ level ] ) as TEnumValue [ ] ; } export function assertInRange < T extends EnumType , TEnumValue extends EnumType > ( value , enumVariable ) { if ( value === undefined ) { throw new Error ( 'undefined enum value' ) ; } if ( ! ( value in enumVariable ) ) { throw new RangeError ( `enum value "${ value }" is out of range` ) ; } }