不,消费者需要使用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
布莱。对不起。
回顾一下: