使用对象字面量作为 TypeScript 枚举值
Posted
技术标签:
【中文标题】使用对象字面量作为 TypeScript 枚举值【英文标题】:Use object literal as TypeScript enum values 【发布时间】:2017-05-01 23:02:05 【问题描述】:我有一个枚举:
export enum PizzaSize
SMALL = 0,
MEDIUM = 1,
LARGE = 2
但在这里我想使用一些值:例如SMALL
我想说它有0
的key
和100
的value
。我努力使用:
export enum PizzaSize
SMALL = key: 0, value: 100 ,
// ...
但是 TypeScript 不接受这个。我该怎么做?
【问题讨论】:
我认为它还不支持..github.com/Microsoft/TypeScript/issues/1206 自 2016 年以来对此有何变化? 【参考方案1】:仅限 TypeScript supports numeric or string-based enums,因此您必须使用类模拟对象枚举(这将允许您将其用作函数声明中的类型):
export class PizzaSize
static readonly SMALL = new PizzaSize('SMALL', 'A small pizza');
static readonly MEDIUM = new PizzaSize('MEDIUM', 'A medium pizza');
static readonly LARGE = new PizzaSize('LARGE', 'A large pizza');
// private to disallow creating other instances of this type
private constructor(private readonly key: string, public readonly value: any)
toString()
return this.key;
那么你可以使用预定义的实例来访问他们的value
:
const mediumVal = PizzaSize.MEDIUM.value;
或您可能想在PizzaSize
中定义的任何其他属性/属性类型。
并且由于 toString()
覆盖,您还可以从对象中隐式打印枚举名称/键:
console.log(PizzaSize.MEDIUM); // prints 'MEDIUM'
【讨论】:
比公认的答案更优雅,但是构造函数应该是私有构造函数(私钥:字符串,公共值:任意),以便能够使用 PizzaSize.MEDIUM.value 或定义一个 getter给它 真@Flohlich。刚刚添加了一个 getter 来禁止更改这样的枚举常量。 @Alexander Mills 没有枚举声明,这就是重点:由于不支持对象枚举,您必须通过类声明来模拟它。 我建议也声明静态项目readonly
。否则,他们可以在外部重新分配 (PizzaSize.SMALL = PizzaSize.LARGE; // get a big pizza, but pay small :-)
)。
如果您使用Ramda
/lodash
克隆您的对象,请小心使用此方法,例如R.clone(pizza: PizzaSize.MEDIUM)
或_.cloneDeep
。三重等于和 switch
语句不会像使用 TypeScript enum
那样工作【参考方案2】:
更新:在下方找到@Javarome's answer,更优雅。我建议用他的方式。
如果您需要使用 Type,请尝试添加一些代码。
用法:getPizzSizeSpec(PizzaSize.small).value
enum PizzaSize
small,
medium,
large
interface PizzaSizeSpec
key: number,
value: number
function getPizzaSizeSpec(pizzaSize: PizzaSize): PizzaSizeSpec
switch (pizzaSize)
case PizzaSize.small:
return key:0, value: 25;
case PizzaSize.medium:
return key:0, value: 35;
case PizzaSize.large:
return key:0, value: 50;
【讨论】:
查看我最近的答案,它还具有详尽检查的好处 这个答案与对象文字枚举相去甚远。 ***.com/a/51398471/2103767 是迄今为止最好的 @Javarome【参考方案3】:从Typescript 3.4 开始,您可以使用keyof typeof
和const
assertions 的组合来创建可以与枚举具有相同类型安全性的对象,并且仍然可以保存复杂值。
通过创建与const
同名的type
,您可以进行与普通枚举相同的详尽检查。
唯一的缺点是你需要复杂对象中的一些键(我在这里使用value
)来保存枚举成员的名称(如果有人能找出一个可以在类型安全的方式,我很想看到它!我无法让一个工作)。
export const PizzaSize =
small: value: 'small', key: 0, size: 25 ,
medium: value: 'medium', key: 1, size: 35 ,
large: value: 'large', key: 2, size: 50 ,
as const
export type PizzaSize = keyof typeof PizzaSize
// if you remove any of these cases, the function won't compile
// because it can't guarantee that you've returned a string
export function order(p: PizzaSize): string
switch (p)
case PizzaSize.small.value: return 'just for show'
case PizzaSize.medium.value: return 'just for show'
case PizzaSize.large.value: return 'just for show'
// you can also just hardcode the strings,
// they'll be type checked
export function order(p: PizzaSize): string
switch (p)
case 'small': return 'just for show'
case 'medium': return 'just for show'
case 'large': return 'just for show'
在其他文件中这个可以简单的使用,只需要导入PizzaSize
即可。
import PizzaSize from './pizza'
console.log(PizzaSize.small.key)
type Order = size: PizzaSize, person: string
还要注意即使是通常可变的对象也不能使用as const
语法进行变异。
const Thing =
ONE: one: [1, 2, 3]
as const
// this won't compile!! Yay!!
Thing.ONE.one.splice(1, 0, 0)
【讨论】:
+1const ... as const
,这对我来说是一个新的!这种方法还可以,但比 Javarome 的方法更难推理
这似乎有点棘手。而且它不允许您像类那样向“枚举”值添加函数。但这会在类没有的地方进行详尽的检查,所以我担心这只是一种权衡选择。
仅供参考,我的 tsc 3.9.7
正在抱怨 order(PizzaSize.large)
和 Argument of type ' readonly value: "large"; readonly key: 2; readonly size: 50; ' is not assignable to parameter of type '"small" | "medium" | "large"'
。所以它的行为不像枚举那样。
是的,这个解决方案并不完全像枚举,它只是并行的,使用 const
对象的键而不是实际的枚举值。通过order(PizzaSize.large.value)
或order('large')
,一切都会编译。这样做就像枚举一样类型安全。
在以类型安全的方式构建枚举对象的值方面,我能想到的最好的办法就是按照我定义的方式转换每个声明。它比我想要的要冗长一些,但似乎可以完成工作。【参考方案4】:
我认为要达到你想要的,这样的事情会起作用
interface PizzaInfo
name: string;
cost_multiplier: number;
enum PizzaSize
SMALL,
MEDIUM,
LARGE,
const pizzas: Record<PizzaSize, PizzaInfo> =
[PizzaSize.SMALL]: name: "Small", cost_multiplier: 0.7 ,
[PizzaSize.MEDIUM]: name: "Medium", cost_multiplier: 1.0 ,
[PizzaSize.LARGE]: name: "Large", cost_multiplier: 1.5 ,
;
const order = PizzaSize.SMALL;
console.log(pizzas[order].name); // "Small"
【讨论】:
如果忘记修改pizzas
,给PizzaSize
添加值,有什么方法会导致编译失败?【参考方案5】:
Object.freeze 使其只读并防止添加更多属性:
const pizzaSize = Object.freeze(
small: key: 0, value: 25 ,
medium: key: 1, value: 35 ,
large: key: 2, value: 50
)
【讨论】:
只有第一级。您还应该冻结每个更深层次。【参考方案6】:您可以使用类型化的 const 来实现:
export const PizzaSize:
[key: string]: key: string, value: string ;
=
SMALL: key: 'key', value: 'value'
;
您可以选择将类型信息提取到单独的接口声明中:
interface PizzaSizeEnumInstance
key: string;
value: string;
interface PizzaSizeEnum
[key: string]: PizzaSizeEnumInstance;
export const PizzaSize: PizzaSizeEnum =
SMALL: key: 'key', value: 'value'
;
【讨论】:
以上是关于使用对象字面量作为 TypeScript 枚举值的主要内容,如果未能解决你的问题,请参考以下文章
在 TypeScript 中动态访问 const 对象字面量