【问题标题】:Template Literal type equivalent模板文字类型等效
【发布时间】:2022-06-17 01:48:20
【问题描述】:

我有一个像这样的枚举:

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">

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

问题是模板文字类型是在 ts 4.1 上发布的,当前捆绑器的 ts 版本是 3.9.7,我无法真正更新它,因为它是外部提供的。

我尝试过复制这种类型:

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

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

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

【问题讨论】:

  • 这里的用例是什么?您将这些类型同时用作枚举和字符串文字类型似乎很奇怪;如果您选择一种用途并坚持使用它,您可以以一种直接的方式编写您的ApiActions 类型。 Observe here。这对您有用吗,还是出于某种原因您需要 ApiFunctions 成为枚举但 ApiActions 成为字符串文字的联合?
  • 这属于一个更广泛的公司项目,我无法真正深入了解,但基本上这些 API 函数属于一个 API,我们将其插入 iframe 以在某些事件发生时触发一些逻辑窗户。发出请求将触发 API 调用,但是。枚举背后的原因是后端的数据结构,但老实说,如果不超过我们目前使用的建议,您的建议也同样有意义。
  • 所以你想让我把它写下来作为答案吗?我认为所问问题的答案是“不,在 TS4.1 之前,没有办法将字符串枚举类型扩展到其相应的字符串文字”,但更有用的方法是坚持使用枚举(并查看字符串值对 TS 代码不透明)或字符串文字。让我知道我应该如何进行。
  • 您可以继续添加它作为答案,因为您所说的完全有道理,尽管不是正确答案,但您提出了具有有效论证的不同解决方案

标签: javascript typescript types bundler template-literals


【解决方案1】:

不,在 TypeScript 4.1 之前,没有编程方式将 string enum types 扩展为对应的 string literal types


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

这意味着您应该能够更改这些值,并且 TypeScript 代码中的任何内容都不会中断。当然,如果这些值对某些外部系统(如 API)很重要,那么与该系统的通信将不再有效。但反过来说,如果外部系统更改为需要"CONCEAL" 而不是"HIDE",则您的TS 代码中唯一需要更改的是setHidden = "CONCEAL";您不需要使用 "CONCEAL" 文字对 "HIDE" 字符串文字进行全局查找和替换。

毕竟,正如您所见,编译器并不容易引用枚举的字面值。它认为ApiFunctions.setHidden"HIDE"更具体,因此您不能在需要ApiFunctions.setHidden 类型的值的地方使用"HIDE" 类型的值。与编译器斗争以从另一个产生一个是为了可疑的好处而付出的很多努力。如果可能的话,让枚举保持枚举。

考虑到这一点,我对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,很容易得到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" */

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

Playground link to code

【讨论】:

  • 这么详细的回答。非常感谢您的帮助 jcalz!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-28
  • 2011-07-15
  • 1970-01-01
相关资源
最近更新 更多