- Unify test data for nonexistence of an object/string and collection. - Introduce more test through adding missing test data to existing tests. - Improve logic for checking absence of values to match tests. - Add missing tests for absent value validation. - Update documentation to include shared test functionality.
64 lines
2.0 KiB
TypeScript
64 lines
2.0 KiB
TypeScript
// Because we cannot do "T extends enum" 😞 https://github.com/microsoft/TypeScript/issues/30611
|
|
export type EnumType = number | string;
|
|
export type EnumVariable<T extends EnumType, TEnumValue extends EnumType>
|
|
= { [key in T]: TEnumValue };
|
|
|
|
export interface IEnumParser<TEnum> {
|
|
parseEnum(value: string, propertyName: string): TEnum;
|
|
}
|
|
|
|
export function createEnumParser<T extends EnumType, TEnumValue extends EnumType>(
|
|
enumVariable: EnumVariable<T, TEnumValue>,
|
|
): IEnumParser<TEnumValue> {
|
|
return {
|
|
parseEnum: (value, propertyName) => parseEnumValue(value, propertyName, enumVariable),
|
|
};
|
|
}
|
|
|
|
function parseEnumValue<T extends EnumType, TEnumValue extends EnumType>(
|
|
value: string,
|
|
enumName: string,
|
|
enumVariable: EnumVariable<T, TEnumValue>,
|
|
): 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
|
|
<T extends EnumType, TEnumValue extends EnumType>(
|
|
enumVariable: EnumVariable<T, TEnumValue>,
|
|
): string[] {
|
|
return Object
|
|
.values(enumVariable)
|
|
.filter((enumMember) => typeof enumMember === 'string') as string[];
|
|
}
|
|
|
|
export function getEnumValues<T extends EnumType, TEnumValue extends EnumType>(
|
|
enumVariable: EnumVariable<T, TEnumValue>,
|
|
): TEnumValue[] {
|
|
return getEnumNames(enumVariable)
|
|
.map((level) => enumVariable[level]) as TEnumValue[];
|
|
}
|
|
|
|
export function assertInRange<T extends EnumType, TEnumValue extends EnumType>(
|
|
value: TEnumValue,
|
|
enumVariable: EnumVariable<T, TEnumValue>,
|
|
) {
|
|
if (value === undefined || value === null) {
|
|
throw new Error('absent enum value');
|
|
}
|
|
if (!(value in enumVariable)) {
|
|
throw new RangeError(`enum value "${value}" is out of range`);
|
|
}
|
|
}
|