Issue
I have an environment variable coming into my app as a string and have built a config method to validate and return an enum value based on enum key (from string):
import { LedMatrix, RowAddressType, MuxType } from 'rpi-led-matrix'
// Validate and return valid MatrixOptions.rowAddressType
export const configRowAddressType = (
configRowAddressType?: string,
): RowAddressType => {
if (!configRowAddressType) {
return LedMatrix.defaultMatrixOptions().rowAddressType
}
const rowAddressType = configRowAddressType as keyof typeof RowAddressType
const keys = Object.keys(RowAddressType)
if (keys.includes(rowAddressType)) {
return RowAddressType[rowAddressType]
}
if (rowAddressType) {
console.error(
`supplied rowAddressType key of ${rowAddressType} is not a valid option, assigning default of ${
LedMatrix.defaultMatrixOptions().rowAddressType
}.`,
)
}
return LedMatrix.defaultMatrixOptions().rowAddressType
}
It works. However, I have another method that looks very similar that validates and types another variable representing another enum key:
// Validate and return valid MatrixOptions.multiplexing
export const configMultiplexing = (configMultiplexing?: string): MuxType => {
if (!configMultiplexing) {
return LedMatrix.defaultMatrixOptions().multiplexing
}
const multiplexing = configMultiplexing as keyof typeof MuxType
const keys = Object.keys(MuxType)
if (keys.includes(multiplexing)) {
return MuxType[multiplexing]
}
if (multiplexing) {
console.error(
`supplied multiplexing key of ${multiplexing} is not a valid option, assigning default of ${
LedMatrix.defaultMatrixOptions().multiplexing
}.`,
)
}
return LedMatrix.defaultMatrixOptions().multiplexing
}
I'll have a total of five or so of these similar methods. It seems redundant, yet I'm struggling with how to return a dynamic enum type. Here's a rough example that could work, perhaps not ideal:
export const configEnumValueByKey = (inputValue: string, enumType: RowAddressType | MuxType | SomethingElse | AnotherSomething | MoreSomething | YetAnother): RowAddressType | MuxType | SomethingElse | AnotherSomething | MoreSomething | YetAnother => {
// ...
}
Is there a way I can refactor for a single method that handles dynamically set return types?
Solution
I was working on something similar, so I expanded it for you. I'm not sure what to say, so here are some resources if you're interested. Function overload Type generics
Edit: fix to return defaultValue if the rawValue is one of the enum values
//Enums
enum Locations {
Address1 = 0,
Address2 = 1,
Address3 = 'SDF'
}
enum Actions {
Scan = 0,
Print = 1,
SelfDestruct = 2
}
//Wrappers around the actual validate function, which can be left untouched
const configLocations = (location?: string) => {
return validateEnum(Locations, location, Locations.Address1);
}
const configActionType = (actionType?: string) => {
//No default value
return validateEnum(Actions, actionType);
}
const val = configLocations('SDF'); //Will be the default value, accurate type
const val2 = configActionType('Print')
console.log(val, val2);
//Generic type for the enum list
type Enum<E> = Record<keyof E, number | string> & { [k: number]: string };
// Use overloads to correctly type return value, if `defaultValue` is present.
// Quirk: `rawValue` cannot be an optional parameter because we want to type `defaultValue` as required. Must specify undefined explicitly in some cases
function validateEnum<E extends Enum<E>>(enumList: E, rawValue: string | undefined): E[keyof E] | undefined;
function validateEnum<E extends Enum<E>>(enumList: E, rawValue: string | undefined, defaultValue: E[keyof E]): E[keyof E];
function validateEnum<E extends Enum<E>>(
enumList: E,
rawValue: string | undefined,
defaultValue?: E[keyof E]
) : E[keyof E] | undefined {
if (rawValue === undefined) {
return defaultValue;
}
// Object.keys on an enum also returns the values (for non-string values). console.log(Object.keys(Locations)) -> ["0", "1", "Address1", "Address2", "Address3"]
// We can just filter out the keys that are not parsable as numbers (numeric enum keys are not allowed anyways).
const enumKeys = Object.keys(enumList).filter(k => isNaN(Number(k)) === true);
if (enumKeys.includes(rawValue)) {
return enumList[rawValue as keyof E];
}
console.error(
`supplied rowAddressType key of ${rawValue} is not a valid option` +
(defaultValue ? `. Replacing with a default value of: ${defaultValue}.` : '')
);
return defaultValue;
}
Answered By - thurasw
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.