【问题标题】:How do I return a typescript enum value based on enum key (from string) for multiple enum types?如何基于枚举键(来自字符串)为多种枚举类型返回打字稿枚举值?
【发布时间】:2022-06-11 16:34:18
【问题描述】:

我有一个环境变量作为字符串进入我的应用程序,并构建了一个配置方法来验证和返回基于枚举键(来自字符串)的枚举值:

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
}

它有效。但是,我有另一种看起来非常相似的方法,它验证并键入另一个表示另一个枚举键的变量:

// 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
}

我将总共有五种左右这些类似的方法。这似乎是多余的,但我正在努力解决如何返回动态枚举类型。这是一个可以工作的粗略示例,可能并不理想:

export const configEnumValueByKey = (inputValue: string, enumType: RowAddressType | MuxType | SomethingElse | AnotherSomething | MoreSomething | YetAnother): RowAddressType | MuxType | SomethingElse | AnotherSomething | MoreSomething | YetAnother => {
  // ...
}

有没有一种方法可以重构处理动态设置返回类型的单个方法?

【问题讨论】:

  • LedMatrix.defaultMatrixOptions()的返回类型是什么?
  • LedMatrix.defaultMatrixOptions() 类型为MatrixOptions
  • 那么RowAddressType/MuxType的类型是什么?是Record 还是别的什么?
  • 这些是枚举:export declare enum RowAddressType { /** * Corresponds to direct setting of the row. */ Direct = 0, /** * Used for panels that only have A/B. (typically some 64x64 panels) */ AB = 1, /** * Direct row select */ DirectRow = 2, /** * ABC addressed panels */ ABC = 3, /** * 4 = ABC Shift + DE direct */ ABCShift = 4 }
  • export declare enum MuxType { Direct = 0, Stripe = 1, Checker = 2, Spiral = 3, ZStripe = 4, ZnMirrorZStripe = 5, Coreman = 6, Kaler2Scan = 7, }

标签: typescript


【解决方案1】:

我正在做类似的事情,所以我为你扩展了它。我不知道该说什么,所以如果您有兴趣,这里有一些资源。 Function overloadType generics

编辑:修复在 rawValue 是枚举值之一时返回 defaultValue

//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;
}

【讨论】:

  • 我觉得这符合赏金的要求,我从中学到了很多东西。谢谢你。我所做的唯一更改是if (!rawValue) { return defaultValue },因为环境变量可能以空字符串(假值)的形式出现。
  • @ChristopherStevens 我想我应该让你知道,如果你尝试console.log(configLocations('0') === Locations.Address1),你会得到错误的。如果 rawValue 是“0”、“1”或“2”或任何枚举“值”,即使它无效,它也不会被默认值替换。这可能会产生很大的误导,因为您将在 Typescript 中拥有一个不等于任何枚举成员的有效枚举类型。
  • 这是因为枚举在 JS 中的映射方式。字符串枚举成员不会发生这种情况。因此,我们可以使用Object.keys 并过滤掉可解析为数字的值。我已经更新了代码 sn-p。
猜你喜欢
  • 2014-07-23
  • 2019-09-26
  • 2020-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-04
相关资源
最近更新 更多