请查看以下简单代码:
const enum MyEnum {
Zero
}
const foo: MyEnum.Zero = 0 // OK as expected (since MyEnum.Zero is zero)
const bar: MyEnum.Zero = 1 // OK, but expected Error! Why?
在这种情况下,如何强制使用精确的窄数类型,即0
?
UPD:枚举似乎被破坏了https://github.com/microsoft/TypeScript/issues/11559
答案 0 :(得分:4)
the enum
section of the current TypeScript handbook中并没有真正提及它,但是所有number
值都可以分配给任何数字枚举类型。新的TypeScript手册的当前草稿显示the following:
由于历史上的限制,一些[assignability]规则很奇怪。例如,任何数字都可分配给数字枚举,但是对于字符串枚举则不是这样。只能将已知为字符串枚举一部分的字符串分配给它。那是因为数字枚举在联合类型和文字类型之前存在,所以它们的规则本来就宽松一些。
过去,似乎使用TypeScript中的数字枚举来支持bit fields,使用位掩码和按位运算来组合显式声明的枚举值以获得新值:
enum Color {
Red = 0xFF0000,
Green = 0x00FF00,
Blue = 0x0000FF
}
const yellow: Color = Color.Red | Color.Green; // 0xFFFF00
const white: Color = Color.Red | Color.Green | Color.Blue; // 0xFFFFFF
const blue: Color = white & ~yellow; // 0x0000FF
并且由于这种枚举的用法存在于现实世界的代码中,因此对alter this behavior的重大改变。以及don't seem particularly inclined to try语言的维护者。
因此,无论好坏,数字枚举的类型大致都与number
同义。
可以滚动您自己的更严格的枚举式对象,但是它涉及手工完成许多使用enum
语法时自动发生的事情。这是一种可能的实现方式(不会给您reverse mapping):
namespace MyEnum {
export const Zero = 0;
export type Zero = typeof Zero;
export const One = 1;
export type One = typeof One;
export const Two = 2;
export type Two = typeof Two;
}
type MyEnum = typeof MyEnum[keyof typeof MyEnum];
const foo: MyEnum.Zero = 0 // okay
const bar: MyEnum.Zero = 1 // error!
它的工作原理如下:当您编写enum X { Y = 123, Z = 456 }
时,TypeScript在运行时引入一个名为X
的值,其属性为X.Y
和X.Z
。它还引入了名为X
,X.Y
和X.Z
的类型。类型X.Y
和X.Z
只是值X.Y
和X.Z
的类型。但是类型X
不是值X
的类型。相反,它是属性类型X.Y | X.Z
的并集。
我使用上面的namespace
,export
,const
和type
来达到类似的效果。但是这里的区别在于,数字枚举的可分配性规则不适用,因此您可以进行严格的类型检查。
好的,希望能有所帮助;祝你好运!
答案 1 :(得分:1)
如@jcalz所述,在数字枚举的情况下,Typescript不会区分number
和MyEnum.X
:
任何数字都可以分配给数字枚举,但是对于字符串枚举则不是这样
那真是可悲...
基本上,这意味着您根本无法完全依靠MyEnum.Zero
的“类型”。因此,您有两种选择:
enum MyEnum {
three = 3,
four,
five,
}
type EnumKeysToTrue = { [ P in MyEnum]: true }; // { 3: true; 4: true; 5: true; }
type TrueObject<T extends MyEnum> = Pick<EnumKeysToTrue, T>;
const a0: TrueObject<MyEnum.three> = { 3: true }; // OK, as expected
const a1: TrueObject<MyEnum.three> = { 4: true }; // Error
const a2: TrueObject<3> = { [MyEnum.three]: true }; // OK, as expected, just in case
const a3: TrueObject<4> = { [MyEnum.three]: true }; // Error, as expected, just in case
在这里,我们实际上是在存储一个对象,它的单个键是我们想要的枚举值,其值是true
。
这可能不是理想的,因为通过a{x}
从这些Object.keys(a1)[0]
变量中获取值可能不是首选方法;但是,它在某些情况下仍然可以使用。
enum MyEnum {
three = 3,
four,
five,
}
type EnumKeysToTrue = { [ P in MyEnum]: true }; // { 3: true; 4: true; 5: true; }
type Enumified<T extends number> = EnumKeysToTrue[T] extends true ? T : never;
const v0: Enumified<MyEnum.three> = MyEnum.four; // Error: Type 'MyEnum.four' is not assignable to type 'MyEnum.three'.ts(2322)
const v1: Enumified<MyEnum.three> = 4; // Unfortunately, OK. So don't use it this way!
const v2: Enumified<4> = MyEnum.three; // Type 'MyEnum.three' is not assignable to type '4'
const v3: Enumified<9> = 9; // error: type 9 is not assignable to never
const v4: Enumified<4> = MyEnum.four; // OK, as expected :)
let num: number = 9;
const v5: Enumified<typeof num> = MyEnum.four; // Type 'MyEnum.four' is not assignable to type 'never'.ts(2322)
这可能会更好,因为一次可以获取v{x}
变量的值并从那里开始! 请注意,必须将分配的值传递给Enumified<>
,并且枚举必须在分配的RHS上,否则,就像上面的v1
一样,它会无意中起作用
PS。您无法为代码库中的所有枚举概括EnumKEyToTrue
。每个枚举实例必须为一个。