将字符串转换为枚举类型的通用方法

帕特里克

我需要接受用户输入并断言该值是一个有效的枚举值。例如,我有一个枚举:

enum Gender {
  Male = 'MALE',
  Female = 'FEMALE',
  Neutral = 'NEUTRAL'
}

用户可以在那里输入任何值,Gender如果值是有效的,我想断言该值是a Gender

拥有这样的功能会很方便:

function toEnum<T, E extends typeof T>(enumType: E, value: string): T | null {
  if (Object.values(enumType).includes(value)) {
    return value as T;
  }

  return null;
}

我可以这样称呼它:

const a: Gender = toEnum(Gender, 'FEMALE'); // === Gender.Female
const b: Gender = toEnum(Gender, 'APPLE'); // null;

我真的不知道哪种通用签名最有意义:

function toEnum<T, E extends typeof T>(enumType: E, value: string): T | null { /* ... */ }

function toEnum<T>(enumType: typeof T, value: string): T | null { /* ... */ }

function toEnum<E, T instanceof E>(enumType: E, value: string): T | null { /* ... */ }

我知道那些事情是无效的。我只是想表达我的意图。

无论如何,要使打字稿推断出T型将是一个枚举值,同时又保持类型安全,最不容易破解的方法是什么?有没有更好的方法为字符串枚举编写类型安全的反向映射函数?

编辑:

一些其他示例可以证明我想实现的目标。我想推断类型,T以便可以消除奇怪的行为,并且我想避免使用,any以便维护类型安全。

enum Gender {
  Male = 'MALE',
  Female = 'FEMALE',
  Neutral = 'NEUTRAL'
}

type Maybe<T> = T | null;

export function toEnum<T>(enumType: any, value: string): Maybe<T> {
  if (Object.values(enumType).includes(value)) {
    return value as unknown as T;
  }

  return null;
}

const shouldBeFemale: Maybe<Gender> = toEnum<Gender>(Gender, 'FEMALE'); // Gender.Female
const shouldBeNull: Maybe<Gender> = toEnum<Gender>(Gender, 'NOT VALID'); // null

// but this is lame
enum Fruit {
  Apple = 'APPLE',
  Orange = 'ORANGE'
}

const wtfItIsApple: Maybe<Gender> = toEnum<Gender>(Fruit, 'APPLE'); // 'APPLE' . . . wat!?
const validButStillSilly: Maybe<Gender> = toEnum<Gender>(Fruit, 'NOT VALID'); // null
const ughAny: Maybe<Gender> = toEnum<Gender>(217, ':('); // null
香农杰克逊
type IsEnumKey<T, E> = [E] extends [keyof T] ? true : false;
function toEnum<T, E extends (string | keyof T)>(enumType: T, value: E): IsEnumKey<T, E> extends true ? E : E | null {
  if (Object.values(enumType).includes(value)) {
    return value as any;
  }

  return null as any;
}

enum Fruit {
    APPLE = "APPLE",
    ORGANE = "ORANGE",
}

const testType = toEnum(Fruit, "APPLE") // Not or null just "Apple";
const testType1 = toEnum(Fruit, "") // "" | null

让我知道是否为您这样做,这样的条件返回类型是以必须在方法主体中进行强制转换为代价的(可能避免强制转换或使用类型保护),但是获得了对返回类型的额外控制。

已知的枚举成员不会将null作为可能的返回类型返回,但是,如果您希望这种情况发生,只需更改返回类型

未知的枚举成员将返回其类型为union的联合。

编辑:这解决了吗?

function toEnum<T, E extends (string | keyof T)>(enumType: T, value: E): [E] extends [keyof T] ? T[E] : E | null {
  if (Object.values(enumType).includes(value)) {
    return value as any;
  }

  return null as any;
}

enum Fruit {
    APPLE = "APPLE",
    ORGANE = "ORANGE",
}

const testType = toEnum(Fruit, "APPLE") // works;
const testType1 = toEnum(Fruit, "") // "" | null
const testType2: Fruit.APPLE = toEnum(Fruit, "APPLE") // works

最终编辑:您可以使用重载而不是条件返回类型来解决在函数体中进行强制转换的问题,以下是一种经过修改的更好的解决方案,其中方法主体中不再需要直接强制转换。

function toEnum<T, E extends keyof T>(enumType: T, value: E): T[E];
function toEnum<T, E extends string>(enumType: T, value: E): E | null;
function toEnum<T, E extends string>(enumType: T, value: E): E | null {
  if (Object.values(enumType).includes(value)) {
    return value as E;
  }

  return null;
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章