为什么TypeScript的Enum会出现问题

目录
  • 发生了什么呢?
  • 什么时候用
  • 控制枚举的数字
  • Bit值
  • 控制索引
  • 非数字枚举
  • 结论

TypeScript引入了很多静态编译语言的特性,比如class(现在是JavaScript的一部分了),interface, generics和union types等。

但是今天有一个类型需要着重讨论下,这就是enum。

对于很多的静态语言来说,枚举是一个很非常常见的语言特性。比如,c,c#,java和swift。枚举就是你在代码里可以用的一组常量。

我们用TypeScript来新建一个enum来代表一周的几天:

enum DayOfWeek {
  Sunday,
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday
};

这个枚举使用enum关键字声明,后面跟着DayOfWeek名称。然后我们定义枚举里可以使用的常量。

现在我们定义一个方法,接受这个枚举类型的参数,来判断传入的参数是不是周末。

function isItTheWeekend(day: DayOfWeek) {
  switch (day) {
    case DayOfWeek.Sunday:
    case DayOfWeek.Saturday:
      return true;

    default:
      return false;
  }
}

最后,我们可以这要用:

console.log(isItTheWeekend(DayOfWeek.Monday)); // log: false

对于消除程序里的魔法字符串来说,这是一个非常有用的方法。

但是,事情远不是我们想的这么简单。下面的代码调用会在TypeScript编译之后得到什么呢?

console.log(isItTheWeekend(2)); // is this valid?

知道结果你会吓一跳。这样的调用是符合TypeScript规则的,编译器也会顺利编译。

发生了什么呢?

上面的情况可能会让你认为你发现了一个TypeScript的bug。其实TypeScript就是这么设计的。

我们这里新建了一个数字枚举,而且我们可以在TypeScript Playground里看看编译出来的结果是什么:

var DayOfWeek;
(function (DayOfWeek) {
    DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday";
    DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday";
    DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday";
    DayOfWeek[DayOfWeek["Wednesday"] = 3] = "Wednesday";
    DayOfWeek[DayOfWeek["Thursday"] = 4] = "Thursday";
    DayOfWeek[DayOfWeek["Friday"] = 5] = "Friday";
    DayOfWeek[DayOfWeek["Saturday"] = 6] = "Saturday";
})(DayOfWeek || (DayOfWeek = {}));

运行结果是:

事实上枚举就是一个JavaScript对象。

这个对象的属性就是根据我进定义的枚举常量生成,还根据定义的顺序生成了对应的数字(顺序,Sunday是0,Saturday是6)。这个对象也有数字作为key,对应的常量字符串作为值的属性。

因此,我们可以给上面的方法传入数字,数字映射到对应的枚举值。枚举既是一个数字常量也是一个字符串常量。

什么时候用

如果一个方法接收一个枚举类型参数,但是一个任意的数字就可以通过编译的话。这样的结果显然破坏了TypeScript构建的类型安全体系。这什么时候可以用呢?

假设你有一个服务返回一个JSON串,你想把这个串建模,对应的某个属性是一个枚举。

在你的数据库里存的是数字。定义一个TypeScript枚举可以很容易解决这个问题:

const day: DayOfWeek = 3;

这个在赋值时执行的显示的类型转换会把数字转换成枚举的对应常量。也就是说我们在代码里使用这个枚举会让代码更容易读懂。

控制枚举的数字

枚举的成员对应的数字是根据枚举常量定义的顺序生成的。那我们是否可以控制这个数字的值呢?是可以的。

enum FileState {
  Read = 1,
  Write = 2
}

只是描述一个文件可能的状态的枚举。

它可能是读也可能是写状态,我们显示的定义了枚举值。现在就很明确什么样的值是合理的,因为显示定义了。

Bit值

但是还有另一个情况很有用,位值(Bit)。

我们再来看一下这个FileState枚举,给它添加一个新的枚举值ReadWrite:

enum FileState {
  Read = 1,
  Write = 2,
  ReadWrite = 3
}

之后假设有一个方法接受这个类型的参数:

const file = await getFile("/path/to/file", FileState.Read | FileState.Write);

我们在FileState上使用了|操作符。这样我们可以使用位运算来获得一个新的枚举值。在这个例子里面就是3,ReadWrite的值。

事实上,我们可以写的更清楚一些:

enum FileState {
  Read = 1,
  Write = 2,
  ReadWrite = Read | Write
}

这个ReadWrite的值不是写死的,而是位运算得到的。

但是再这样使用枚举的时候要多加小心。

如下的枚举:

enum Foo {
  A = 1,
  B = 2,
  C = 3,
  D = 4,
  E = 5
}

如果要得到E(或者5),可以位运算得到么:Foo.A | Foo.D or Foo.B | Foo.C?

所以如果要用枚举值做位运算,那么明确如何得到这个值。

控制索引

一般情况下,每个枚举值都会有一个默认的数字值。如果需要也可以明确的给这些枚举值赋值。另外,还可以给某部分枚举赋值:

enum DayOfWeek {
  Sunday,
  Monday,
  Tuesday,
  Wednesday = 10,
  Thursday,
  Friday,
  Saturday
}

前几个值是按照位置赋值,Sunday到TuesDay是0到2.之后在Wednesday给了一个新值,从这开始每个值都递增1. 这就可能会出现问题了:

enum DayOfWeek {
  Sunday,
  Monday,
  Tuesday,
  Wednesday = 10,
  Thursday = 2,
  Friday,
  Saturday
}

Tuesday赋值为2,生成的JavaScript是什么样子呢:

var DayOfWeek;
(function (DayOfWeek) {
    DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday";
    DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday";
    DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday";
    DayOfWeek[DayOfWeek["Wednesday"] = 10] = "Wednesday";
    DayOfWeek[DayOfWeek["Thursday"] = 2] = "Thursday";
    DayOfWeek[DayOfWeek["Friday"] = 3] = "Friday";
    DayOfWeek[DayOfWeek["Saturday"] = 4] = "Saturday";
})(DayOfWeek || (DayOfWeek = {}));

看起来Tuesday和Thursday的数值都是2。

所以,需要显示的设定数值。

非数字枚举

目前为止,我们只讨论了数值枚举,但是枚举的值不一定非的是数字。它也可以是任何常量或者计算值:

enum DayOfWeek {
  Sunday = "Sun",
  Monday = "Mon",
  Tuesday = "Tues",
  Wednesday = "Wed",
  Thursday = "Thurs",
  Friday = "Fri",
  Saturday = "Sat"
}

现在就不能给isItTheWeekend方法穿数字参数了。这个枚举已经不再是数字枚举。然而,我们也不能传任意字符串进去,因为枚举知道什么样的值才是合理的。

这样也带来另外一个问题:

const day: DayOfWeek = "Mon";

这样是行不通的。

字符串并不能直接给枚举赋值,而是需要一个显示的类型转换:

const day = "Mon" as DayOfWeek;
能不能给它赋其他值呢?事实上枚举可以有很多类型的值:

enum Confusing {
  A,
  B = 1,
  C = 1 << 8,
  D = 1 + 2,
  E = "Hello World".length
}

这个例子的枚举值都是数字。但是这些数字值可以直接赋值,也可以是计算值,或者是字符串的length属性。如果都是常量的话,那么就可以是多种类型的值:

enum MoreConfusion {
  A,
  B = 2,
  C = "C"
}

这种情况就很难让人理解枚举后面的数据是怎么工作的。所以,最好不要用这样的枚举。

结论

TypeScript的枚举是对JavaScript的一个很好地补充,使用得当将非常有用。它将有助于清理代码中存在的魔术值(magic values)字符串、数字。而且它是类型安全的。

到此这篇关于为什么TypeScript的Enum会出现问题的文章就介绍到这了,更多相关TypeScript Enum内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一文搞懂TypeScript的安装、使用、自动编译的教程

    1. 初识 TypeScript 上篇文章给大家介绍过TypeScript的安装.使用.自动编译的实现  需要的朋友点击查看. TypeScript 的介绍 TypeScript 是一种由微软开发的开源.跨平台的编程语言.它是 JavaScript 的超集,最终会被编译为 JavaScript 代码. 2012 年 10 月,微软发布了首个公开版本的 TypeScript,2013 年 6 月 19 日,在经历了一个预览版之后微软正式发布了正式版 TypeScript TypeScript 的作

  • 详解TypeScript映射类型和更好的字面量类型推断

    概述 TypeScript 2.1 引入了映射类型,这是对类型系统的一个强大的补充.本质上,映射类型允许w咱们通过映射属性类型从现有类型创建新类型.根据咱们指定的规则转换现有类型的每个属性.转换后的属性组成新的类型. 使用映射类型,可以捕获类型系统中类似Object.freeze()等方法的效果.冻结对象后,就不能再添加.更改或删除其中的属性.来看看如何在不使用映射类型的情况下在类型系统中对其进行编码: interface Point { x: number; y: number; } inte

  • Typescript中的as、问号与感叹号详解

    1.as关键字表示断言 在Typescript中,表示断言有两种方式.一种是扩号表示法: let someValue: any = "this is a string"; let strLength: number = (someValue).length; 另一种使用as关键字: let someValue: any = "this is a string"; let strLength: number = (someValue as string).length

  • 教你使用webpack打包编译TypeScript代码

    TypeScript打包 webpack整合 通常情况下,实际开发中我们都需要使用构建工具对代码进行打包: TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS: 步骤如下: 初始化项目 进入项目根目录,执行命令 npm init -y,创建package.json文件 下载构建工具 命令如下: npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader clean-webpac

  • 深入理解typescript中的infer关键字的使用

    infer 这个关键字,整理记录一下,避免后面忘记了.有点难以理解呢. infer infer是在 typescript 2.8中新增的关键字. infer 可以在 extends 条件类型的字句中,在真实分支中引用此推断类型变量,推断待推断的类型. 例如:用infer推断函数的返回值类型 type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; type fn = () => number type

  • TypeScript 中如何限制对象键名的取值范围

    当我们使用 TypeScript 时,我们想利用它提供的类型系统限制代码的方方面面,对象的键值,也不例外. 譬如我们有个对象存储每个年级的人名,类型大概长这样: type Students = Record<string, string[]>; 理所当然地,数据就是长这样: const students: Students = { Freshman: ["David", "John"], sophomore: [], Junior: ["Lil

  • 为什么TypeScript的Enum会出现问题

    目录 发生了什么呢? 什么时候用 控制枚举的数字 Bit值 控制索引 非数字枚举 结论 TypeScript引入了很多静态编译语言的特性,比如class(现在是JavaScript的一部分了),interface, generics和union types等. 但是今天有一个类型需要着重讨论下,这就是enum. 对于很多的静态语言来说,枚举是一个很非常常见的语言特性.比如,c,c#,java和swift.枚举就是你在代码里可以用的一组常量. 我们用TypeScript来新建一个enum来代表一周

  • TypeScript 学习笔记之基本类型

    在 TypeScript 中一共有 7 种基本类型. 1.boolean 复制代码 代码如下: var isDone: boolean = false; 2.number 代表 JavaScript 中的数字.在 JavaScript 中,无论是"整数"还是"浮点数",都是以双精度浮点类型存储的. 复制代码 代码如下: var height: number = 6; 3.string 代表字符串.跟 JavaScript 一样,可以使用一对双引号(")或一

  • TypeScript入门-基本数据类型

    大致介绍 TypeScript是由C#语言之父Anders Hejlsberg主导开发的一门编程语言,TypeScript本质上是向JavaScript语言添加了可选的静态类型和基于类的面向对象编程,它相当于是JavaScript的超集 ES5.ES6和TypeScript的关系: 安装 首先需要安装npm,然后在输入 npm install -g typescript 安装完成后,因为TypeScript是以.ts结尾的,要想运行就得把他编译js文件,编译的方法特别简单就是使用tsc命令 ts

  • 深入理解JavaScript和TypeScript中的class

    前言 对于一个前端开发者来说,很少用到 class ,因为在 JavaScript 中更多的是 函数式 编程,抬手就是一个 function,几乎不见 class 或 new 的踪影.所以 设计模式 也是大多数前端开发者的一个短板. 最近在学习 Angular的过程中发现其大量的运用了 class,不得不佩服,Angular 确实是一个优秀的.值得深入研究的 框架. 本文将简单的介绍一下 JavaScript 和 TypeScript 中的 class. 基本概念 在介绍 class 之前,要先

  • 详解Vue3.0 前的 TypeScript 最佳入门实践

    前言 我个人对更严格类型限制没有积极的看法,毕竟各类转类型的骚写法写习惯了. 然鹅最近的一个项目中,是 TypeScript + Vue ,毛计喇,学之...-真香! 注意此篇标题的"前",本文旨在讲Ts混入框架的使用,不讲Class API 1. 使用官方脚手架构建 npm install -g @vue/cli # OR yarn global add @vue/cli 新的 Vue CLI 工具允许开发者 使用 TypeScript 集成环境 创建新项目. 只需运行 vue cr

  • 使用Angular9和TypeScript开发RPG游戏的方法

    RPG系统构造 通过对于斗罗大陆小说的游戏化过程,熟悉Angular的结构以及使用TypeScript的面向对象开发方法. 项目地址 人物 和其他RPG游戏类似,游戏里面的人物角色大致有这样的一些属性:生命值,魔法值(魂力),攻击力,防御力,速度.RPG游戏中的角色随着等级的提高,这些属性都会提升,属性提升的快慢则取决于资质,同时,由于在实际战斗中,会出现各种增益和光环效果,这些值都是动态变化的,所以这里将这些属性都设置了Base和Real两套数据. Base属性是指人物的初始属性,是一种固有属

  • TypeScript魔法堂之枚举的超实用手册

    前言 也许前端的同学会问JavaScript从诞生至今都没有枚举类型,我们不是都活得挺好的吗?为什么TypeScript需要引入枚举类型呢? 也许被迫写前端的后端同学会问,TypeScript的枚举类型是和Java/.NET的一样吗? 下面我们来一起探讨和尝试解答吧! 前端一直都需要枚举 我敢保证,前端的同学都会万分肯定地告诉大家:我们从来没有写过枚举.那是因为虽然ECMAScript将enum作为保留字,但至ES2020为止还没有提出枚举的实现规范.语言没有提供规范和语言实现,不代表思想活跃勇

  • vue3+typescript实现图片懒加载插件

    github项目地址: github.com/murongg/vue- 求star 与 issues 我文采不好,可能写的文章不咋样,有什么问题可以在留言区评论,我会尽力解答 本项目已经发布到npm 安装: $ npm i vue3-lazyload # or $ yarn add vue3-lazyload 需求分析 支持自定义 loading 图片,图片加载状态时使用此图片 支持自定义 error 图片,图片加载失败后使用此图片 支持 lifecycle hooks,类似于 vue 的生命周

  • TypeScript 安装使用及基本数据类型

    第一步 全局安装TypeScript 使用 npm 安装 npm install -g typescript 使用cnpm 安装 cnpm install -g typescript 使用yarn安装 yarn global add typescript 第二步 初始化TypeScript 在vscode里面 终端 >> 运行生成任务 >> tsc:监视tsconfig.json 接下来就可以开始我们的typescript旅程了~ TypeScript 的 基本数据类型 // 布尔

随机推荐