在打字稿中获取枚举键作为联合字符串的通用类型?

2024-01-04

考虑以下打字稿枚举:

enum MyEnum { A, B, C };

如果我想要另一种类型,即该枚举键的联合字符串,我可以执行以下操作:

type MyEnumKeysAsStrings = keyof typeof MyEnum;  // "A" | "B" | "C"

这非常有用。

现在我想创建一个以这种方式对枚举进行普遍操作的泛型类型,这样我就可以说:

type MyEnumKeysAsStrings = AnyEnumKeysAsStrings<MyEnum>;

我想正确的语法是:

type AnyEnumKeysAsStrings<TEnum> = keyof typeof TEnum; // TS Error: 'TEnum' only refers to a type, but is being used as a value here.

但这会产生编译错误:“‘TEnum’仅指类型,但在这里被用作值。”

这是出乎意料且令人悲伤的。我可以通过以下方式不完全地解决这个问题:从泛型声明的右侧删除 typeof,并将其添加到特定类型声明中的类型参数中:

type AnyEnumAsUntypedKeys<TEnum> = keyof TEnum;
type MyEnumKeysAsStrings = AnyEnumAsUntypedKeys<typeof MyEnum>; // works, but not kind to consumer.  Ick.

不过,我不喜欢这种解决方法,因为这意味着消费者必须记住在泛型上执行这种令人讨厌的 typeof 指定。

是否有任何语法允许我指定我最初想要的泛型类型,以善待消费者?


不,消费者需要使用typeof MyEnum引用其键为的对象A, B, and C.


前面有很长的解释,其中一些您可能已经知道

您可能知道,TypeScript 在 JavaScript 中添加了一个静态类型系统,并且该类型系统得到erased https://www.typescriptlang.org/docs/handbook/2/basic-types.html#erased-types当代码被转译时。 TypeScript 的语法是这样的,一些表达式和语句引用values存在于运行时,而其他表达式和语句引用types仅在设计/编译时存在。价值观have类型,但它们本身并不是类型。重要的是,在代码中的某些位置,编译器会期望一个值,并在可能的情况下将其找到的表达式解释为值,而在其他位置,编译器将期望一个类型,并在可能的情况下将其找到的表达式解释为类型。

编译器不关心表达式是否可以同时解释为值和类型,也不会感到困惑。例如,它非常高兴有两种口味null在下面的代码中:

let maybeString: string | null = null;

第一个实例null一个是类型,第二个是值。它也没有问题

let Foo = {a: 0};
type Foo = {b: string};   

第一个在哪里Foo是一个命名值,第二个Foo是一个命名类型。注意值的类型Foo is {a: number},而类型Foo is {b: string}。他们不一样。

即便是typeof操作员过着双重生活。表达方式typeof x总是期待x成为一个价值, but typeof x其本身可以是一个值或类型,具体取决于上下文:

let bar = {a: 0};
let TypeofBar = typeof bar; // the value "object"
type TypeofBar = typeof bar; // the type {a: number}

线路let TypeofBar = typeof bar;将会到达 JavaScript,并且它将使用JavaScript typeof 运算符 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof在运行时并产生一个字符串。但type TypeofBar = typeof bar;被擦除,并且它正在使用TypeScript 类型查询运算符 https://github.com/Microsoft/TypeScript/blob/v2.8.3/doc/spec.md#3.8.10检查 TypeScript 分配给名为的值的静态类型bar.


现在,TypeScript 中引入名称的大多数语言结构都会创建命名值或命名类型。以下是命名值的一些介绍:

const value1 = 1;
let value2 = 2;
var value3 = 3;
function value4() {}

以下是命名类型的一些介绍:

interface Type1 {}
type Type2 = string;

但有一些声明会创建both命名值and命名类型,并且,就像Foo above, 命名值的类型不是命名类型。大的是class and enum:

class Class { public prop = 0; }
enum Enum { A, B }

在这里,type Class是一个类型instance of Class,而value Class is the 构造函数目的。和typeof Class is not Class:

const instance = new Class();  // value instance has type (Class)
// type (Class) is essentially the same as {prop: number};

const ctor = Class; // value ctor has type (typeof Class)
// type (typeof Class) is essentially the same as new() => Class;

并且,type Enum是一个类型element枚举;每个元素的类型的联合。虽然value Enum is an object谁的钥匙是A and B,其属性是枚举的元素。和typeof Enum is not Enum:

const element = Math.random() < 0.5 ? Enum.A : Enum.B; 
// value element has type (Enum)
// type (Enum) is essentially the same as Enum.A | Enum.B
//  which is a subtype of (0 | 1)

const enumObject = Enum;
// value enumObject has type (typeof Enum)
// type (typeof Enum) is essentially the same as {A: Enum.A; B: Enum.B}
//  which is a subtype of {A:0, B:1}

现在支持你的问题。您想发明一个像这样工作的类型运算符:

type KeysOfEnum = EnumKeysAsStrings<Enum>;  // "A" | "B"

你把type Enum并获取钥匙object Enum出去。但正如你在上面看到的,方式Enum与对象不一样Enum。不幸的是,该类型对值一无所知。这有点像这样说:

type KeysOfEnum = EnumKeysAsString<0 | 1>; // "A" | "B"

显然,如果你这样写,你会发现你无法对该类型做任何事情0 | 1这会产生类型"A" | "B"。为了使其工作,您需要向其传递一个了解映射的类型。这种类型是typeof Enum...

type KeysOfEnum = EnumKeysAsStrings<typeof Enum>; 

这就像

type KeysOfEnum = EnumKeysAsString<{A:0, B:1}>; // "A" | "B"

which is可能...如果type EnumKeysAsString<T> = keyof T.


所以你陷入了让消费者指定的困境typeof Enum。有解决方法吗?好吧,您也许可以使用具有该值的东西,例如函数?

 function enumKeysAsString<TEnum>(theEnum: TEnum): keyof TEnum {
   // eliminate numeric keys
   const keys = Object.keys(theEnum).filter(x => 
     (+x)+"" !== x) as (keyof TEnum)[];
   // return some random key
   return keys[Math.floor(Math.random()*keys.length)]; 
 }

然后你可以打电话

 const someKey = enumKeysAsString(Enum);

和类型someKey"A" | "B"。是的,但是然后将其用作type你必须查询它:

 type KeysOfEnum = typeof someKey;

这迫使你使用typeof再次,比你的解决方案更详细,特别是因为你不能这样做:

 type KeysOfEnum = typeof enumKeysAsString(Enum); // error

布莱。对不起。


回顾一下:

  • 这不可能
  • 类型和值等等
  • 还是不可能
  • Sorry
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在打字稿中获取枚举键作为联合字符串的通用类型? 的相关文章

随机推荐

  • 使用多线程写入文件

    我正在尝试使用多个线程在 Java 中编写一个大文件 我都尝试过FileWriter and bufferedWriterJava 中的类 正在写入的内容实际上是使用读取的整个表 Postgres CopyManager并写下 文件中的每一
  • matplotlib 中的图例设置(numpoints 和 scatterpoints)不起作用

    我试图让图例适合虚线 所以我稍微使用了 rcParams 但由于某些原因它无法在我的计算机上工作 import numpy as np import matplotlib pyplot as plt import matplotlib ma
  • kCGColorSpaceGenericRGB 在 iPhone 上已弃用?

    我正在尝试使用以下代码获取位图上下文 GContextRef MyCreateBitmapContext int pixelsWide int pixelsHigh CGContextRef context NULL CGColorSpac
  • UILabel 根据要显示的文本自动调整大小

    我正在开发一个应用程序 其中需要根据要显示的文本自动调整文本区域的大小 首先 我不确定我是否应该使用UILabel 逻辑上是显示静态文本的最佳选择 就我而言 或UITextView 我希望如何使用它 我想简单地用文本初始化我的标签或文本视图
  • 如何在 django 中将列表从视图传递到模板

    我正在尝试将列表从 Django 中的视图传递到模板 在我的文件 wiew py 中 我定义了名为 hour 的视图 This Python file uses the following encoding utf 8 from djang
  • NSKeyedArchiver 和 NSKeyedUnarchiver 与 NSMutableArray

    我希望这与我在这里使用可变数组这一事实无关 但这让我感到困惑 所以如果是这种情况 我也不会感到惊讶 背景 我制作了一个小型数据库 它本质上是一个包含自定义对象的 NSMutableArray 我们可以将其称为 recordObjects 我
  • 如何在 MAC OS X 应用程序中更改 NSTableView 标题背景颜色?

    我已经尝试了所有找到的建议解决方案 但最终将此作为最接近的 目标是为以下对象提供自定义颜色 完整的标题背景 例如绿色 文字 例如白色 排序控件颜色 例如白色 目前 我只能正确设置内部背景和文本颜色 同时将标题边框和排序控件保留为默认白色 我
  • 为什么vertical-align: middle不能与表格单元格中的输入元素一起使用?

    这是我的代码 vertical align top margin 0 td vertical align middle border 1px solid red td nth child 1 line height 3em td nth c
  • 在sql中转换excel百分位公式

    PERCENTILE C2 C11080 E2 E11080 G2 G11080 73 上面的公式是Excel百分位数公式 我想将其转换为SQL 11080是该列的计数 如果有人可以帮助我 那将会非常有帮助 我相信percentile co
  • PhoneGap 和 Cordova 之间的区别以及我们应该安装哪一个?

    我的任务是开发 Android 和 iOS 应用程序PhoneGap http phonegap com http phonegap com 然而 当我读到时 事情开始变得非常复杂 有些人说Cordova http cordova apac
  • 不支持 Hive 方法

    我正在尝试使用 Hive 作为底层数据存储来运行 SQl 查询 该查询调用 Big Decimal 函数并引发以下错误 不支持该方法 org apache hadoop hive jdbc HivePreparedStatement set
  • 以编程方式检测 Android 屏幕覆盖

    有没有办法让应用程序能够 检查其顶部是否存在屏幕覆盖层 以及 找出哪个包名称拥有覆盖层 我知道 Android M 及更高版本能够在权限页面中检测屏幕覆盖 并在检测到屏幕覆盖时拒绝权限更改 但开发人员是否能够在应用程序层实现相同的功能 您可
  • 在 Python 中随时插入解释器

    我知道如何使用口译员pdb and IPython 但这需要我事先确切地知道我想在哪里停止 然而 我经常运行需要几分钟到几小时的数字处理脚本 我想确切地知道它的进展情况 一种解决方案是简单地在各处放置大量日志记录语句 但随后我要么被太多信息
  • 数据库更新后nodejs自动刷新视图

    每次函数对数据库进行更改时 我想使用 nodeJS 刷新我的视图 如果我们以 MEAN stack 为例 我不想每 x 秒发送一个 http request 来检查数据库是否已进行更改 我希望前端能够自动收到通知 然后更新视图 这方面的最佳
  • clang 和 gcc 不实现 std::hardware_{constructive,virtual}_interference_size 的原因是什么?

    我知道答案可能是他们没有优先考虑它 但这确实感觉像是故意遗漏 他们已经拥有大量 C 20 核心语言 库功能 而这个 C 17 功能仍然没有实现 其实按照这个table https en cppreference com w cpp comp
  • 如何抑制Windows命令输出?

    我通过命令行移动数千个文件 并将所有文件名打印到控制台 有没有办法忽略输出以便它可以更快 类似于 Linux 中到 dev null 的管道 Thanks bla bla bla bla bla bla bla bla 2 gt nul
  • java泛型中的类型见证

    我从 Java 文档中的 Generics Trail 中看到什么是类型见证 BoxDemo
  • 如何在网格框中随机填充颜色

    如何在网格框中随机填充颜色 而不是如图所示有序 网格 http www freeimagehosting net uploads 4ed76557de jpg http www freeimagehosting net uploads 4e
  • 如何打印单个 JPanel 的内容?

    我有一个JPanel有两个带图片的标签 我需要打印这些内容JPanel 请帮帮我 我怎样才能只打印这个JPanel的内容 因为我的上也有不同的组件JFrame但我只需要打印this JPanel Thanks 下面是打印任何 Swing 组
  • 在打字稿中获取枚举键作为联合字符串的通用类型?

    考虑以下打字稿枚举 enum MyEnum A B C 如果我想要另一种类型 即该枚举键的联合字符串 我可以执行以下操作 type MyEnumKeysAsStrings keyof typeof MyEnum A B C 这非常有用 现在