枚举打字稿返回特定值

Nik*_*ili 1 javascript typescript

我有一个这样的枚举:

enum Test {
   'type1',
   'type2',
   'type3',
   'type4',
   'type5'
}

function getType(num: number) {

}

Run Code Online (Sandbox Code Playgroud)

我想要的是如果num = 1,返回type1,如果num = 2,返回type2,如果num >= 5,返回type5

我怎样才能做到这一点getType

cap*_*ian 5

这是一个棘手的问题。请参阅相关问题和我的文章

枚举的运行时表示是双向对象。

const Test = {
  0: "type1",
  1: "type2",
  2: "type3",
  3: "type4",
  4: "type5",
  type1: 0,
  type2: 1,
  type3: 2,
  type4: 3,
  type5: 4,
}
Run Code Online (Sandbox Code Playgroud)

请在阅读时牢记这一点。

考虑这个例子:


enum MyEnum {
  ONE, // 0
  TWO // 1
}

// This utility type reverses object. 
// All keys become values an all values become keys
type ReverseObj<T extends Record<string, string | number>> = {
  [Prop in keyof T as T[Prop]]: Prop
}

{
  type _ = ReverseObj<{ age: 42 }> // { 42: "age" }
}

// Type representation of runtime enum value
// Please keep in mind that enum runtime value is bidirectional
// this is why I have used ReverseObject
type EnumToObj = Pick<
  {
    [Prop in keyof typeof MyEnum]: `${typeof MyEnum[Prop]}`
  }, keyof typeof MyEnum
>

type IsKeyValid<
  InitialValue extends number,
  > =
  `${InitialValue}` extends keyof ReverseObj<EnumToObj>
  ? InitialValue
  : never

{
  type _ = IsKeyValid<1> // 1
  type __ = IsKeyValid<2> // never
}

function handleEnum<
  Index extends number,
  >(index: IsKeyValid<Index>): `${Index}` extends keyof ReverseObj<EnumToObj> ? ReverseObj<EnumToObj>[`${Index}`] : never
function handleEnum<
  Index extends number,
  >(index: IsKeyValid<Index>) {
  return MyEnum[index]
}

handleEnum(0) // "ONE"
handleEnum(1) // "TWO"
handleEnum(2) // expected error
handleEnum('ONE') // expected error
handleEnum(0.1) // expected error
handleEnum(NaN) // expected error
handleEnum(Infinity) // expected error
Run Code Online (Sandbox Code Playgroud)

操场

您会在评论中找到一些解释。目标是让非法国家无法代表。

IsKeyValid- 确保不允许您提供枚举中不存在的密钥。例如,您不允许打电话,handleEnum(10)因为我们只有两个键,因此允许的键是0 | 1。至于返回值,我是从reverse object中获取的ReverseObj

您应该注意一些事情:

  1. 类型typeof MyEnumMyEnum不相等。
  2. as有关使用in 的更多说明[Prop in keyof T as T[Prop]]: Prop可以在这里找到
  3. 如果您不使用枚举的自定义初始索引,则值得使用简单元组而不是枚举。考虑这个例子:
const TUPLE = ['one', 'two', 'three'] as const;

type Tuple = typeof TUPLE

type AllowedIndex<Index extends number> = `${Index}` extends Exclude<keyof Tuple, keyof ReadonlyArray<any>> ? Index : never

const getter = <Index extends number>(index: AllowedIndex<Index>) =>
  TUPLE[index]

getter(1) // ok, returns "two"
getter(23) // expected error
Run Code Online (Sandbox Code Playgroud)
  1. 在大多数情况下,使用不可变对象而不是枚举更好。
const FakeEnum = {
  a: 0,
  b: 1
} as const
Run Code Online (Sandbox Code Playgroud)

使用FakeEnum对象很容易。因为你可以轻松地、无需任何技巧地获取对象的键和适当的值。

恕我直言,我认为这不是enum在这种情况下使用的最佳选择

PS如果您不喜欢枚举中的从零开始的索引,您可以设置初始值:

enum MyEnum {
  ONE = 1, // 1
  TWO // 2
}
Run Code Online (Sandbox Code Playgroud)

更新

上述方法有其自身的缺点。您可能已经注意到,所有实用程序类型都与MyEnum. 它们不是通用的。如果您对可以传递任何枚举的通用解决方案感兴趣,请考虑以下示例:


enum MyEnum {
  ONE, // 0
  TWO // 1
}

/**
 * Obtains a union of all dictionary values
 */
type Values<T> = T[keyof T]


/**
 * Represents any enum type
 */
type EnumType = Record<string | number, string | number>

type EnumToObj<Enum extends EnumType> = Pick<
  {
    [Prop in keyof Enum]:
    (Enum[Prop] extends string | number
      ? `${Enum[Prop]}`
      : never)
  }, keyof Enum
>

{
  // {
  //   readonly ONE: "0";
  //   readonly TWO: "1";
  // }
  type Test = EnumToObj<typeof MyEnum>
}

type GetEnumValue<
  Enum extends EnumType,
  Index extends number,
  Obj extends EnumToObj<Enum> = EnumToObj<Enum>
  > =
  {
    [Prop in keyof Obj]:
    (`${Index}` extends Obj[Prop]
      ? Prop
      : never)
  }[keyof Enum];

{
  type Test = GetEnumValue<typeof MyEnum, 1> // TWO
}

type IsNever<T> = [T] extends [never] ? true : false

type IsKeyValid<
  Index extends number,
  Enum extends EnumType
  > =
  IsNever<GetEnumValue<Enum, Index>> extends true
  ? never
  : Index

{
  type _ = IsKeyValid<1, typeof MyEnum> // 1
  type __ = IsKeyValid<2, typeof MyEnum> // never
}

function handleEnum<
  Index extends number,
  Enum extends EnumType
>(
  enEnum: Enum,
  index: IsKeyValid<Index, Enum>
): GetEnumValue<Enum, Index>
function handleEnum<
  Index extends number,
  Enum extends EnumType
>(enEnum: Enum, index: IsKeyValid<Index, Enum>) {
  return enEnum[index]
}

const x = handleEnum(MyEnum, 0) // "ONE"
const y = handleEnum(MyEnum, 1) // "TWO"

handleEnum(MyEnum, 2) // expected error
handleEnum(MyEnum, 'ONE') // expected error


Run Code Online (Sandbox Code Playgroud)

操场