模板文字类型等效

Template Literal type equivalent

早上好,

我有一个这样的枚举:

export enum ApiFunctions {
  "setHidden" = "HIDE",
  "setReadOnly" = "SET_READ_ONLY",
  "setVisible" = "SHOW",
  "setDescription" = "SET_DESCRIPTION",
  "setName" = "SET_NAME",
  "makeRequest" = "MAKE_REQUEST"
}

今天早些时候,我从这个枚举中创建了一个新类型,如下所示:

export type ApiActions = Exclude<`${ApiFunctions}`, "MAKE_REQUEST">

此类型 returns 除“MAKE_REQUEST”之外的键的所有值 (SET_DESCRIPTION,....)

问题是 Template literal types 是在 ts 4.1 上发布的,当前 bundler 的 ts 版本是 3.9.7,我无法真正更新它,因为它是外部提供的。

我尝试通过以下方式复制此类型:

export type Something = Exclude<typeof ApiFunctions[keyof typeof ApiFunctions], "MAKE_REQUEST">

但是这种类型没有给我每个键 SET_NAME | SET_DESCRIPTION ... 的实际字符串值,而是给我一些 ApiFunctions.setName | ApiFunctions.setDescription ...

行中的内容

有没有办法以任何其他方式实现与模板文字创建的类型完全相同的类型?

谢谢

不,在 TypeScript 4.1 之前,没有编程方式来扩展 string enum types to their corresponding string literal types


但是,您可能不想做这样的事情(即使使用 TypeScript 4.1 及更高版本)并且可能有更好的方法。 TypeScript enums 最适合它们的值对您的 TypeScript 代码库 不透明 的情况。因此,除了 enum 定义本身,您的代码 不应该关心 它们的特定值。访问这些值的唯一方法是通过枚举。所以总是 ApiFunctions.setHidden 而永远不会 "HIDE".

这意味着您应该能够更改值,并且 TypeScript 代码中的任何内容都不应中断。当然,如果这些值对某些外部系统很重要,例如 API,那么与该系统的通信将不再有效。但相反,如果外部系统发生变化,它需要 "CONCEAL" 而不是 "HIDE",那么您唯一需要在 TS 代码中更改的是 setHidden = "CONCEAL";您不需要使用 "CONCEAL" 文字对 "HIDE" 字符串文字进行全局 find-and-replace。

毕竟,如您所见,编译器不会让引用枚举的文字值变得容易。它认为 ApiFunctions.setHidden"HIDE" 更具体 ,因此您不能使用 "HIDE" 类型的值,其中值类型 ApiFunctions.setHidden 是必需的。与编译器斗争以从另一个产生一个是为了可疑的利益而付出的大量努力。如果可能的话,让枚举保持枚举。

考虑到这一点,我对 ApiActions 的建议是:

export enum ApiFunctions {
  setHidden = "HIDE",
  setReadOnly = "SET_READ_ONLY",
  setVisible = "SHOW",
  setDescription = "SET_DESCRIPTION",
  setName = "SET_NAME",
  makeRequest = "MAKE_REQUEST"
}

export type ApiActions = Exclude<ApiFunctions, ApiFunctions.makeRequest>;
/* type ApiActions = ApiFunctions.setHidden | ApiFunctions.setReadOnly | 
     ApiFunctions.setVisible | ApiFunctions.setDescription | ApiFunctions.setName */

另一方面,如果您的 TypeScript 代码确实 关心特定的字符串文字类型,那么 enum 可能不适合您的用例。自 TypeScript 3.4 引入 const assertions, it's been pretty easy to get an enum-like object。它看起来像这样:

export const ApiFunctions = {
  setHidden: "HIDE",
  setReadOnly: "SET_READ_ONLY",
  setVisible: "SHOW",
  setDescription: "SET_DESCRIPTION",
  setName: "SET_NAME",
  makeRequest: "MAKE_REQUEST"
} as const;

由此您可以通过编程方式创建一个 union type 也称为 ApiFunctions:

export type ApiFunctions =
  typeof ApiFunctions[keyof typeof ApiFunctions];

这两个定义的行为类似于 ApiFunctions 的枚举版本,除了现在编译器允许您通过枚举和文字类型引用值:

export type ApiActions = Exclude<ApiFunctions, "MAKE_REQUEST">;
/* type ApiActions = "SET_NAME" | "HIDE" | "SET_READ_ONLY" | "SHOW" | "SET_DESCRIPTION" */

哪种方法(如果有的话)更适合您取决于您​​的用例。我倾向于尽可能地完全避免使用 enum,因为它们不是 JavaScript 的一部分,并且它们具有令人惊讶的行为(尤其是数字枚举)。但这是个人喜好。

Playground link to code