在打字稿中,为什么枚举的联合不是枚举?
我有两个想要联合的枚举。
enum Color {
RED = 'red',
BLUE = 'blue',
}
enum Shape {
CIRCLE = 'circle',
SQUARE = 'square',
}
但是,当我声明联合类型并尝试引用成员时:
type ColorShape = Color | Shape;
const test: ColorShape = ColorShape.RED;
我收到编译时错误:
TS2693:“ColorShape”仅指一种类型,但在此处用作值。
这是为什么呢?枚举的联合本身不也是一个枚举吗?
I have two enums that I would like to union.
enum Color {
RED = 'red',
BLUE = 'blue',
}
enum Shape {
CIRCLE = 'circle',
SQUARE = 'square',
}
However when I declare a union type and attempt to reference a member:
type ColorShape = Color | Shape;
const test: ColorShape = ColorShape.RED;
I get a compile time error:
TS2693: 'ColorShape' only refers to a type, but is being used as a value here.
Why is this? Would an union of enums not also be an enum itself?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
重要的是要记住 TypeScript 之间的 TypeScript 类型之间的差异,这些差异在编译时就存在,并且是 从发出的 JavaScript 代码中删除;和 JavaScript 值,它们也存在于运行时并出现在发出的 JavaScript 代码中。 TypeScript 尝试跟踪值和类型,但 JavaScript 只知道值。
大多数时候,当您在 TypeScript 中进行声明时,您要么将一个值带入作用域,要么将一个类型带入作用域,但不能同时带入两者:
在上面,
foo
和bar
是存在于发出的 JavaScript 中的值,而Baz
和Qux
是被删除的类型。请注意,值和类型具有完全不同的命名空间;您可以拥有同名的值和类型,并且它们不必彼此相关。编译器不会混淆它们:
这里
Xyz
是值的名称(具有z
属性且值为1
的对象),并且也是类型的名称(具有y
属性且值类型为string
的对象类型)。这些是无关的。如果我编写const uvw = Xyz;
,那么我正在编写使其成为 JavaScript 的值级代码,因此Xyz
就是值。如果我编写interface Rst extends Xyz { }
,那么我正在编写从 JavaScript 中删除的类型级代码,因此Xyz
就是类型。好的,所以大多数时候,当您声明某个命名事物时,该事物要么是类型,要么是值,但不能同时是两者。但是有两个声明确实产生了同名的值和类型:
class
声明 和枚举
声明:这里名称
Abc
既指您可以在运行时引用的类构造函数 value,如new Abc()
,它还指一个类您可以在编译时使用实例类型来讨论类实例,例如const实例:Abc = ...
。如果您编写const instance: Abc = new Abc()
,您将首先引用类型,然后引用值。同样,名称
Def
指的是您可以在运行时引用的枚举对象 value,例如Def.D === "e"
,它还引用一个枚举类型,它是枚举成员值类型的联合,例如const def:Def = ...
。如果您编写const def: Def = Def.D
,您将首先引用类型,然后引用值。现在,虽然
class
是 JavaScript 的一部分(自 ES2015 起),但enum
不是。因此,当您编写enum
时,编译器基本上会 将其降级为一些 JavaScript。让我们看看您的Color
枚举:这或多或少会编译成一些行为就像
它只是一个普通对象。同时,
Color
类型的行为类似于这并不完全正确,因为
enum
已被“标记”,因此您不能只为它们分配一个字符串,但它很接近足以满足我们的目的。因此,当您的enum Color
声明类似于上述值和类型声明的组合时。现在,当您编写时,
您正在创建一个
ColorShape
type。但type ColorShape = ...
不是enum
,因此您所做的就是创建类型。这里没有相应的价值产生。您只完成了创建类似枚举的组合事物的一半工作。如果您想要相应的值,则必须手动创建它。您想要的是看起来像
{RED: "red", BLUE: "blue", CIRCLE: "circle", SQUARE: "square"}
的东西。有多种方法可以获取Color
值和Shape
值并创建这样的对象。您可以使用
Object.assign( )
方法:或者你可以使用 对象传播:
无论哪种方式都应该有效。完成此操作后,现在,您(大部分)可以使用
ColorShape
,就好像它是由Color 组成的组合
和enum
Shape
:请注意,我说的是“大部分”。
enum
声明还将更多类型引入范围;它实际上充当namespace
,您可以在其中使用枚举值的虚线路径本身就是类型:但是您的 ColorShape 定义中的任何一个都不会发生这种情况:
您可能不关心这一点。如果您这样做,那么我知道如何执行此操作的唯一方法是手动创建您的 ColorShape 命名空间并从中导出每种类型:
不太好,可能不是您需要的任何内容。但为了完整起见,我想我只是展示
enum
的幕后还有更多的事情发生。<一href="https://www.typescriptlang.org/play?#code/PTAEDcEMBsFcFMDOBYAUAYwPYDtEBdQAzTTUAXlAEYAmAZgG41DZt08BLHUAI0gCcA FAEpQAb1ABfNGhCg8ATwaOSNAuWgAQpABe5MaEgAuUNlgBbbvD6TGqdtjxXCkdPFABFWAA993Y-j57AHNJaVRZRHhFfkhHE0gzJGjXFAwcfFAADXld CnFtY0obVSU3bNz9eX88QOwQiVsw2W5MPAALNHRoSEREUABBbnR9YYoAIjHQ1HhTM1AAEXhCfXm9MfhJqVQwmfNQAGFMaExrUTRQC9AAJQBRVYoAcj 54ABMHgBpzy40AGQBVG56B7cODwD5oLZoXZzADKbUg6jOqEuBwAkld9j9AY90Ow+F0wZ9kZcYe4-v1bkDEABHWD8QkQsJqNyHY58OEItwUVknUAAH1 AHOUjXCACpOukCDz2fD1BQAPLcABW8DYADoeoh2EFsAJRBJ3gcjidDUL4EJbLIsLgpcaZZzjMzMMtpaAAGRyUrOwWy+BoUXAMLWjLSs16cRqyPSw2R tVhhoyUWgYO2tlm4xIlH7dGYm7GM1q7MYrG2FGk8m3fO+unkik3UuXW7zYzStVNhsXX4Alt2tVd+sQ0ABoOSuRIPA9tO+vSh6vt0CyTAAa0g8jC9kc fGcrmurxheFibkzl2wCXg1VqQQ7ybtk5ObbuC7AAB4ALSvuRtdh9b8GT3KRltiA09EkQZIWTtMNjwueAvEUE4CGZNFi2xf94G9WdOULHMSy+GC4IQt DBVrSkKCdF1IOrcs62vWD4L4RDSmuR8yK9Cipyw9s8NAOjCKQ-s9HIo0OOUPt-gHVBISAjcnBcNwrj3A9HGoMRuJA89QACYJrywNk73tUSm0AsIgA" rel="noreferrer">Playground 代码链接
It is important to keep in mind the difference in TypeScript between TypeScript types, which exist at compile time and are erased from the emitted JavaScript code; and JavaScript values, which also exist at runtime and appear in the emitted JavaScript code. TypeScript tries to keep track of values and types, but JavaScript only knows about values.
Most of the time when you make a declaration in TypeScript you are either bringing a value into scope or a type into scope but not both:
In the above,
foo
andbar
are values that exist in the emitted JavaScript, whileBaz
andQux
are types that are erased.And note that values and types have completely different namespaces; you can have a value and a type with the same name, and they don't have to be related to each other. The compiler doesn't confuse them:
Here
Xyz
is the name of a value (an object with az
property whose value is1
) and also the name of a type (an object type with ay
property whose value type isstring
). These are unrelated. If I writeconst uvw = Xyz;
then I'm writing value-level code that makes it to JavaScript and therefore theXyz
is the value. If I writeinterface Rst extends Xyz { }
then I'm writing type-level code that's erased from JavaScript and therefore theXyz
is the type.Okay, so most of the time when you declare some named thing, that thing is either a type or a value but not both. But there are two declarations that do bring into existence both a value and a type of the same name: the
class
declaration and theenum
declaration:Here the name
Abc
refers to both a class constructor value that you can reference at runtime likenew Abc()
, and it also refers to a class instance type that you can use at compile time to talk about class instances likeconst instance: Abc = ...
. And if you writeconst instance: Abc = new Abc()
you are referring first to the type and then to the value.Similarly, the name
Def
refers to both an enum object value that you can reference at runtime likeDef.D === "e"
, and it also refers to an enum type that is the union of the types of the enum member values likeconst def: Def = ...
. And if you writeconst def: Def = Def.D
you are referring first to the type and then to the value.Now, while
class
is part of JavaScript (since ES2015),enum
is not. So when you write anenum
the compiler will essentially downlevel it to some JavaScript. Let's look at yourColor
enum:This more or less compiles to something that acts like
It's just a plain object. Meanwhile the type
Color
is something that acts likeThat's not strictly true because
enum
s are "marked" so that you can't just assign a string to them, but it's close enough for our purposes. So when yourenum Color
declaration is similar to the combination of the above value and type declarations.Now, when you write
You are creating a
ColorShape
type. Buttype ColorShape = ...
is not anenum
, and so all you've done is create the type. There is no corresponding value brought into existence here. You've only done half the work in creating a combinedenum
-like thing. If you want the corresponding value, you will have to create it manually.What you want is something that looks like
{RED: "red", BLUE: "blue", CIRCLE: "circle", SQUARE: "square"}
. There are multiple ways to take theColor
value and theShape
value and make such an object.You could use the
Object.assign()
method:Or you could use object spread:
Either way should work. Once you've done this, now, you can (mostly) use
ColorShape
as if it were a combinedenum
composed ofColor
andShape
:Note that I said "mostly". The
enum
declaration also brings a few more types into scope; it actually acts as anamespace
where you can use the dotted path to enum values as types themselves:But that doesn't happen with either of your
ColorShape
definitions:You probably don't care about that. If somehow you do, then the only way I know how to do this is to manually create your
ColorShape
namespace and export each type from it:Not great, and probably not anything you need. But for completeness I figured I'd just show that there's a bit more going on under the hood with
enum
s.Playground link to code