草庐IT

TS-枚举类型enum

望屿 2024-04-08 原文
  1. 枚举的作用是列举类型中包含的各个值,一般用它来管理多个相同系列的常量(即不能被修改的变量),用于状态的判断。这是一种无序的数据结构,把键映射到值上,可以理解为编译时键固定的对象,访问键时,ts将检查指定的键是否存在

在web中,比较常见的状态判断有响应状态的判断:

const handleResponseStatus = (status: number): void => {
  switch (status) {
    case 200: // 请求成功时
      // Do something...
      break;
    case 400: // 请求失败时
      // Do something...
      break;
    default:
      throw (new Error('No have status code!'));
  }
};

刚刚的200,304等都是没有争议的状态码,但是实际开发过程中往往后台人员会自己定义一些字符串标识状态:

  • 接手项目的程序员不去翻阅文档的话根本不知道这是啥玩意
  • 容易造成魔鬼字符串的问题
  • 用enum可以很好地解决这个问题
enum requestStatusCodes {
    error = '400',
    success = '200',
}
enum requestWrongCodes {
    missingParameter = 'A',
    wrongParameterType = 'B',
    invalidToken = 'C'
}

const handleWrongStatus = (status: string): void => {
  // 如果觉得 requestWrongCodes.missingParameter 太长了,也可以用以下方式:
  const { missingParameter, wrongParameterType, invalidToken, } = requestWrongCodes;
  switch (status) {
    case missingParameter:
      // Do something...
      break;
    case wrongParameterType:
      // Do something...
      break;
    case invalidToken:
      // Do something...
      break;
    default:
      throw (new Error('No have wrong code!'));
  }
};
  1. 枚举分为两种:字符串到字符串之间的映射和字符串到数字之间的映射。即key和value反向对应的对象,如下所示:
enum Language {  //枚举名称为大写的单数形式
    English,   //枚举中的键也为大写
    Spanish,
    Russian
}
console.log(Language);
//ts自动推导出来的Language如下
{
  '0': 'English',
  '1': 'Spanish',
  '2': 'Russian',
  English: 0,    
  Spanish: 1,    
  Russian: 2     
}
  1. 枚举中的值使用点号或者方括号
let myFirstLanguage = Language.English
let mySecondLanguage = Language['Spanish']
console.log(myFirstLanguage, mySecondLanguage); //0 1
  1. 一个枚举可以分成几次声明,ts会自动把各部分合并在一起,注意,如果分开声明枚举,ts只能推导出其中一部分的值,因此最好为枚举中的每个成员显式赋值
enum Language {  
    English = 0,  
    Spanish = 1,
}
enum Language {
    Russian = 2
}
  1. 不必为枚举中的所有成员都赋值,ts会尽其所能推导出缺少的值:
enum Language { 
    English = 1,   
    Spanish = 100 + 1,
    Russian  //ts推导为102
}
console.log(Language);
  1. 枚举的值可以为字符串,甚至混用字符串和数字
enum Color {
    Red = '#c10000',
    Blue = '#007ac1',
    Pink = 'oxc10050',
    White = 255
}
let red = Color['Red']
let white = Color.White
console.log(red, white);  //#c10000 255
  1. ts比较灵活,允许通过值访问枚举,也允许通过键访问枚举,不过容易导致问题
enum Language {  
    English,   
    Spanish,
    Russian,
}
console.log(Language[0], Language[1]);  //English Spanish


Language[‘Chinese’]和 Language[6]明明都不存在,为什么前者报错后者不报错呢?

  1. 利用js实现enum
  • 实现key-value的反向对应的对象
  • 不可修改
const newEnum = (descriptions) => {
    const result = {}
    Object.keys(descriptions).forEach(description => {
        console.log(result[description] = descriptions[description]);//注意,等号这个会返回等于的值
        result[result[description] = descriptions[description]] = description;
    });
    return Object.freeze(result)
}
let responseCode = newEnum({
    error: 400,
    success: 200
})
console.log(responseCode);

可以看到,enum本质上是生成键值对双向映射的对象,所以访问color[6]按照obj的惯性,是不会报错的,之后返回undefined
其实Color[6]不存在,但是TypeScript并不阻止你这么做。为了避免这种不安全的访问操作:

  1. 可以通过const enum指定使用枚举的安全子集。下面使用该方法重写前面的Language枚举
  • const enum 不允许通过键反向查找,行为和常规的js对象很像
  • 另外,默认不编译生成任何js代码(就像第八点生成的那个result对象),而是在用到枚举成员的地方内插对应的值,例如,把Lauguage.Spanish直接替换成1

  • 使用const emun的好处,加入需要使用的enum特别多,那在执行时就会不停地使用 IIFE 产生 Object 将 Key 和 Value 绑定到 Object,会造成一些效率上的损失,也会增加内存,但是 const 并不会产生 Object ,也就不会有以上的问题。
  • 就算到的 Enum 不多,判断时也需要一直从 Object 中找出对应的值,而如果是用 const 声明 Enum ,在编译成 JS 时就将声明的值直接放入判断中。
  1. const enum的内插行为带来的安全问题
  • const enum内插值的行为在从其他人编写的TypeScript代码中导入const enum时可能导致安全问题:假如原作者在你编译TypeScript 代码之后更新了const enum,那么在运行时你使用的枚举与原作者的枚举指向的值可能不同,而TypeScript没有这么智能,无法得知这一变化。
  • 使用const enum时请尽量避免内插,而且只在受自己控制的TypeScript程序中使用。不要在计划发布到NPM中的程序,或者开放给其他人使用的库中使用。

如果想为const enum生成运行时代码,在 tsconfig.json中把TSC选项preserveConstEnums设为true:

参考:

  1. 书籍-《typescript编程》
  2. 木庄的博客

有关TS-枚举类型enum的更多相关文章

  1. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  2. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  5. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  6. ruby - [1,2,3].to_enum 和 [1,2,3].enum_for 在 Ruby 中的区别 - 2

    在Ruby中,我试图理解to_enum和enum_for方法。在我提出问题之前,我提供了一些示例代码和两个示例来帮助理解上下文。示例代码:#replicatesgroup_bymethodonArrayclassclassArraydefgroup_by2(&input_block)returnself.enum_for(:group_by2)unlessblock_given?hash=Hash.new{|h,k|h[k]=[]}self.each{|e|hash[input_block.call(e)]示例#1:irb(main)>puts[1,2,3].group_by2.ins

  7. ruby - 引用具有指定索引的枚举器值 - 2

    假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum

  8. ruby-on-rails - Ruby on Rails,使用同名枚举的问题 - 2

    以下是我认为的一些下拉列表:'form-control')%>和'form-control')%>这是我的application_helper.rbdefget_advance_bookingret=[{:require_booking=>'No'},{:require_booking=>'Yes'}]enddefget_instant_bookingret=[{:instant_booking=>'No'},{:instant_booking=>'Yes'}]end但现在的问题是,在我的模型product.rb中,我无法设置具有相同名称的枚举:classProduct我收到的错误是您

  9. ruby-on-rails - Rails 迁移中的 PostgreSQL 点类型 - 2

    我想使用PostgreSQL中的point类型。我已经完成了:railsgmodelTestpoint:point最终的迁移是:classCreateTests当我运行时:rakedb:migrate结果是:==CreateTests:migrating====================================================--create_table(:tests)rakeaborted!Anerrorhasoccurred,thisandalllatermigrationscanceled:undefinedmethod`point'for#/hom

  10. ruby-on-rails - 我可以用鸭子类型(duck typing)改进这种方法吗? - 2

    希望我没有误解“ducktyping”的含义,但从我读到的内容来看,这意味着我应该根据对象如何响应方法而不是它是什么类型/类来编写代码。代码如下:defconvert_hash(hash)ifhash.keys.all?{|k|k.is_a?(Integer)}returnhashelsifhash.keys.all?{|k|k.is_a?(Property)}new_hash={}hash.each_pair{|k,v|new_hash[k.id]=v}returnnew_hashelseraise"CustomattributekeysshouldbeID'sorPropertyo

随机推荐