获取字符串枚举值的类型

时间:2018-06-16 00:12:50

标签: typescript

我有一个字符串枚举,如下所示:

export enum FMEvents {
  RECORD_ADDED = "@firemodel/RECORD_ADDED",
  RECORD_CHANGED = "@firemodel/RECORD_CHANGED",
  RECORD_MOVED = "@firemodel/RECORD_MOVED",
  RECORD_REMOVED = "@firemodel/RECORD_REMOVED",
}

我希望能够将函数的输入约束到枚举的字符串值(例如," @ firemodel / RECORD_ADDED"等)。

我想我可能只是为方法signture执行以下操作:

public doSomething(event: keyof FMEvents) { ... }

但输入的内容完全错误(我认为它给了我enum对象的键,不确定但绝对错误)。

然后我尝试了:

public doSomething(event: FMEvents) { ... }

这允许我使用doSomething()来呼叫FMEvents.RECORD_CHANGED,但不允许我使用已解析的密钥doSomething("@firemodel/RECORD_CHANGED")来呼叫它。

我正在寻找的是一种将它限制在Enum中定义为值的字符串的方法,而不是其他任何东西。有了这个,我希望上面的两个调用方法都会通过类型检查。

2 个答案:

答案 0 :(得分:3)

TypeScript不容易将枚举值类型扩展为它们派生的字符串或数字文字。 (有一个complication that prevents using intersections to help with this)你可以使用conditional types非常接近你想要的东西:

type Extractable<T, U> = T extends U ? any : never
type NotString<T> = string extends T ? never : any
function promoteStringToFMEvents<K 
  extends string & NotString<K> & Extractable<FMEvents, K>>(
  k: K
): Extract<FMEvents, K> {
  return k;
}

const fmAdded = promoteStringToFMEvents("@firemodel/RECORD_ADDED"); // FMEvents.RECORD_ADDED
const fmOops = promoteStringToFMEvents("@firemodel/RECORD_ADDLED"); // error

在上面的代码中,如果Extractable<T, U>或其任何成分可分配给any,则T会返回U,否则会never。并且NotString<T>返回any T不是string或更宽,否则never。通过将K中的promoteStringToFMEvents()限制为string & NotString<K> & Extractable<FMEvents, K>,我们说类型参数K必须是某些元素(或联合)的字符串文字(或字符串文字的并集) FMEvents的{​​{1}}元素可以分配给。

因此函数promoteStringToFMEvents()将接受您期望的字符串文字(或字符串文字的联合)。该函数还只返回FMEvents的关联元素,方法是将输入值分配给Extract<FMEvents, K>,这样就可以得出与FMEvents匹配的K个。{/ p>

因此,您可以编写doSomething()方法,使其在上面的K类型中是通用的,并且在方法的实现中,您可以(如果需要)将字符串提升为枚举通过将其分配给类型为Extract<FMEvents, K>的变量。

编辑并明确实施doSomething()

class Blomp {
  public doSomething<K
    extends string & NotString<K> & Extractable<FMEvents, K>>(k: K) {
    // k is of some subtype of "@firemodel/RECORD_ADDED" | 
    // "@firemodel/RECORD_CHANGED" | "@firemodel/RECORD_MOVED" |
    // "@firemodel/RECORD_REMOVED"

    // if you need to interpret k as a subtype of FMEvents, you can:
    const kAsFMEvent: Extract<FMEvents, K> = k;

    // or even wider as just FMEvents
    const fmEvent: FMEvents = kAsFMEvent;

    // do what you want here
  }
}

希望有所帮助。祝你好运!

答案 1 :(得分:0)

template literal 运算符的帮助下,可以将枚举的值列表推断为类型:

export enum FMEvents {
  RECORD_ADDED = "@firemodel/RECORD_ADDED",
  RECORD_CHANGED = "@firemodel/RECORD_CHANGED",
  RECORD_MOVED = "@firemodel/RECORD_MOVED",
  RECORD_REMOVED = "@firemodel/RECORD_REMOVED",
}

type FMEventsValue = `${FMEvents}`
// => type FMEventsValue = "@firemodel/RECORD_ADDED" | "@firemodel/RECORD_CHANGED" | ...

const event: FMEventsValue = "@firemodel/RECORD_ADDED"
// => ✅ OK

const event: FMEventsValue = "NoT_iN_ThE_eNuM"
// => ? KO

参考文章:Get the values of an enum dynamically