简体   繁体   English

模板文字类型等效

[英]Template Literal type equivalent

I have an enum like so:我有一个像这样的枚举:

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

Earlier today I created a new type from this enum like so:今天早些时候,我从这个枚举中创建了一个新类型,如下所示:

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

This type returns all the values of the keys except "MAKE_REQUEST" (SET_DESCRIPTION,....)此类型返回键的所有值,除了“MAKE_REQUEST”(SET_DESCRIPTION,....)

The problem is Template literal types were released on ts 4.1 and the current bundler's ts version is 3.9.7 and I can't really update it since it is externally provided.问题是模板文字类型是在 ts 4.1 上发布的,当前捆绑器的 ts 版本是 3.9.7,我无法真正更新它,因为它是外部提供的。

I have tried replicating this type by doing:我尝试通过执行以下操作来复制这种类型:

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

But this type instead of giving me the actual string value of each key SET_NAME | SET_DESCRIPTION ...但是这种类型并没有给我每个键的实际字符串值SET_NAME | SET_DESCRIPTION ... SET_NAME | SET_DESCRIPTION ... gives me something in the lines of ApiFunctions.setName | ApiFunctions.setDescription ... SET_NAME | SET_DESCRIPTION ...在 ApiFunctions.setName ApiFunctions.setName | ApiFunctions.setDescription ...的行中给了我一些东西ApiFunctions.setName | ApiFunctions.setDescription ...

Is there a way of achieving exactly the same type created by the template literal on any other way?有没有办法以任何其他方式实现模板文字创建的完全相同的类型?

No, before TypeScript 4.1 there was no programmatic way to widen string enum types to their corresponding string literal types .不,在 TypeScript 4.1 之前,没有编程方法可以将字符串enum类型扩展为相应的字符串文字类型


However, you probably shouldn't want to do such a thing (even with TypeScript 4.1 and above) and there might be a better approach.但是,您可能不应该做这样的事情(即使使用 TypeScript 4.1 及更高版本)并且可能有更好的方法。 TypeScript enum s are most appropriate for situations where their values are opaque to your TypeScript code base. TypeScript enum最适合它们的值对您的 TypeScript 代码库不透明的情况。 So other than the enum definition itself, your code should not care about their specific values.因此,除了enum定义本身之外,您的代码不应该关心它们的具体值。 The only way you should access these values is through the enum.访问这些值的唯一方法是通过枚举。 So always ApiFunctions.setHidden and never "HIDE" .所以总是ApiFunctions.setHidden而从不"HIDE"

That means you should be able to change the values and nothing inside the TypeScript code should break.这意味着您应该能够更改这些值,并且 TypeScript 代码中的任何内容都不会中断。 Of course, if those values are important to some external system like an API, then communication with that system would no longer work.当然,如果这些值对某些外部系统(如 API)很重要,那么与该系统的通信将不再有效。 But conversely, if the external system changes so that it wants "CONCEAL" instead of "HIDE" , the only thing you should have to change in your TS code is setHidden = "CONCEAL" ;但相反,如果外部系统发生变化,因此它需要"CONCEAL"而不是"HIDE" ,那么您唯一需要在 TS 代码中更改的是setHidden = "CONCEAL" ; you shouldn't need to do a global find-and-replace of the "HIDE" string literal with the "CONCEAL" literal.您不需要使用"CONCEAL"文字对"HIDE"字符串文字进行全局查找和替换。

After all, as you've seen, the compiler doesn't make it easy to refer to the literal values of an enum.毕竟,正如您所见,编译器并不容易引用枚举的文字值。 It takes the view that ApiFunctions.setHidden is more specific than "HIDE" , and so you can't use a value of type "HIDE" where a value of type ApiFunctions.setHidden is required.它认为ApiFunctions.setHidden"HIDE"更具体,因此您不能在需要ApiFunctions.setHidden类型的值的情况下使用"HIDE"类型的值。 And fighting with the compiler to produce one from the other is a lot of effort for questionable benefit.与编译器斗争以从另一个产生一个是为了可疑的好处而付出的很多努力。 Let enums stay enums, if at all possible.如果可能的话,让枚举保持枚举。

With that in mind, my suggestion for ApiActions would be:考虑到这一点,我对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 */

On the other hand, if your TypeScript code really does care about the specific string literal types, then enum might not be right for your use case.另一方面,如果您的 TypeScript 代码确实关心特定的字符串文字类型,那么enum可能不适合您的用例。 Since TypeScript 3.4 introduced const assertions , it's been pretty easy to get an enum-like object .自从 TypeScript 3.4 引入了const assertions以来,很容易获得一个类似 enum 的对象 It looks like this:它看起来像这样:

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

From this you can programmatically make a union type also called ApiFunctions :从此,您可以以编程方式创建一个联合类型,也称为ApiFunctions

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

These two definitions behave similarly to the enum version of ApiFunctions , except now the compiler lets you refer to the values both through the enum and as literal types:这两个定义的行为类似于ApiFunctions的枚举版本,除了现在编译器允许您通过枚举和文字类型引用值:

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

Which approach, if any, works better for you depends on your use case.哪种方法(如果有)更适合您,取决于您的用例。 I tend to avoid enum s entirely wherever I can, because they're not part of JavaScript, and they have surprising behavior (especially numeric enums).我倾向于尽可能完全避免enum ,因为它们不是 JavaScript 的一部分,并且它们具有令人惊讶的行为(尤其是数字枚举)。 But that's personal preference.但这是个人喜好。

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM