快速上手TypeScript中的枚举类型和位运算的认知!

226 阅读5分钟

“我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第4篇文章,点击查看活动详情

扩展类型

基础类型已经适用于绝大数场景,但是扩展类型可以持续优化我们的代码,使得我们效率更高

具体分为:

  • 类型别名
  • 枚举
  • 接口

枚举

定义

枚举通常约束某个变量,某个函数返回值等的取值范围.

其实字面量和联合类型配合使用, 也可以达到同样的目的, 这样的话要枚举做啥呢??

字面量类型的问题

①在类型约束位置,会产生重复代码。可以使用类型别名解决这个问题

    let gender: '男' | '女';

    gender = "女"
    
    // 这个时候重复定义,产生重复代码
    function searchGender(g: '男' | '女') {}
    
    ---
    
    // 利用类型别名解决重复
    type Gender = '男' | '女';
    let gender: Gender

    gender = "女"

    function searchGender(g: Gender) {}

②定义的联合类型的数值。和使用真实的值不一样时,会产生大量的修改

    // 项目经理要求改变类型,觉得男|女太俗了
    type Gender = '李易峰' | '美女';
    let gender: Gender
    
    // 这个时候如果代码有几千行都赋值原来的定义,那要累死个人
    gender = "女"
    gender = "男"

    function searchGender(g: Gender) {}

③字面量类型不会进入编译结果

    // 编译成js后消失, 并且无法在ts中拿到所有定义的值去渲染或循环啥的
    type Gender = '李易峰' | '美女';

如果项目中遇到后两个问题,那只能靠枚举大哥来解决啦

枚举的使用

  • 使用格式
    enum 枚举名 { 
        枚举字段1 = 枚举值1,
        枚举字段2 = 枚举值2
    }

解决第二个问题

    enum Gender { 
        male = '李易峰', 
        female = '美女'
    }
    // 保持不变
    let gender: Gender
    
    gender = Gender.female
    gender = Gender.male
    console.log(Gender.female);  // 美女
    
    // 保持不变
    function searchGender(g: Gender) {}
  • 解决第三个问题枚举会出现在编译结果中,编译结果中表现为对象
    /**
     * 这块内容相当于转化为一个对象
     *  {
     *      male: "\u674E\u6613\u5CF0",
     *      female: "\u7F8E\u5973"
     *  }
     */
    var Gender;
    (function (Gender) {
        // 中文被转化为unicode
        Gender["male"] = "\u674E\u6613\u5CF0";
        Gender["female"] = "\u7F8E\u5973";
    })(Gender || (Gender = {}));
    
    let gender;
    gender = Gender.female;
    gender = Gender.male;
    console.log(Gender.female);
    function searchGender(g) { }

并且我们可以在ts代码里直接打印枚举, 就把他认为是一个对象就完事了

    function print () {
        const vals = Object.values(Gender)
        vals.forEach(v=>console.log(v)) // 李易峰 美女
    }

所以我们在做取值范围的时候,用枚举准没错

枚举细节玩法

  1. 枚举的字段值可以是字符串或数字
    enum Gender { 
        male = [], // 报错: “含字符串值成员的枚举中不允许使用计算值。”
        female = '美女'
    }
  1. 数字枚举的值会自动递增, 每个增加1。 如果第一位不写,默认从0开始递增
    enum level {
        level1= 1,
        level2,
        level3,
    }
    console.log(level2) //2
    console.log(level3) //3
    
    ---
    
    enum level {
        level1,
        level2,
        level3,
    }
    console.log(level1) //0
    console.log(level2) //1
    console.log(level3) //2
  1. 数字枚举约束的变量,可以直接赋值为数字,

这会导致一系列的问题,最好不要这么做

    enum level {
            level1= 1,
            level2,
            level3,
    }

    let a:level = 2
  1. 数字枚举的编译结果 和 字符串的枚举编译结果 有差异
数字枚举便编译结果

var level;
(function (level) {
    level[level["level1"] = 1] = "level1";
    level[level["level2"] = 2] = "level2";
    level[level["level3"] = 3] = "level3";
})(level || (level = {}));
ts源码

    enum level {
            level1= 1,
            level2,
            level3,
    }
    
    console.log(level)
    {
      '1': 'level1',
      '2': 'level2',
      '3': 'level3',
      level1: 1,
      level2: 2,
      level3: 3
    }

枚举的最佳实践

  • 尽量不要在枚举中即出现字符串,又出现数字,这个会引发很多问题的出现
  • 使用枚举值,尽量都使用枚举的字段名

枚举的扩展-位运算

什么是位运算?

是把两个数字换算成二进制后对每一位进行运算

包含这3种形式:

  • 运算: 对每一位去||, 比较两者有一个为1就为1, 否者为0
 let a = 1 | 8 //=>9, => 1001
 // 推理过程如下:
 0001
 1000
 1(0||1) 0(0||0) 0(0||0) 1(1||0)
  • 运算: 对每一位去&&, 比较两者都为1才为1, 否者为0
 let a = 1 & 8 //=>0, => 0000
 // 推理过程如下:
 0001
 1000
 1(0&&1) 0(0&&0) 0(0&&0) 1(1&&0)
  • 异或运算: 对每一位去比较异同, 如果相同=0,若果不同=1
 let a = 1 ^ 8 //=>9, => 1001
 // 推理过程如下:
 0001
 1000
 1(不同) 0(相同) 0(相同) 1(不同)

枚举中使用位运算

位枚举,也称枚举的位运算, 主要是针对数字类型的枚举

我们如何理解呢, 举个例子:

现在有个需求: 需要完成创建删除,四种权限的组合, 我们如何取用枚举实现呢?

  • 第一步-创建枚举
    // 创建一个0-3的数字枚举, 包含读,写,创建,删除
    enum Permission {
	Read,
	Write,
	Create,
	Delete
    }
  • 第二步-设置值
    // 利用2进制数值,去表示组合
    enum Permission {
	Read = 1, //=> 2^0, => 0001
	Write = 2, //=> 2^1, => 0010
	Create = 4, //=> 2^2, => 0100
	Delete = 8 //=> 2^3, => 1000
    }
  • 第三步-组合使用

利用或运算,组合设置权限:

   // 设置可读,可写
   // 注意这里是赋值, 不是定义类型
   // 定义为数字枚举, 可以赋值任意数字
   let p: Permission = Permission.Read | Permission.Write
   let p: Permission = Permission.Read | Permission..Write | Permission.Delete

利用且运算,判断是否包含某个权限:

    let p: Permission = Permission.Read | Permission.Write
    
    function hasPermission(target: Permission, per: Permission) {
        return (target & per) === per
    }
    
    // 判断p有木有可写权限
    hasPermission(p, Permission.Write) // => true

利用且运算,删除某个权限:

    let p: Permission = Permission.Read | Permission.Write
    
    p = p ^ Permission.Write //=> p=1
    hasPermission(p, Permission.Write) // => false