第四章 TypeScript的枚举和类型约束的介绍和使用

image.png

一、枚举

认识枚举

  • 在很多计算机语言中都有枚举的概念,但是 JS 中是没有枚举这个概念的,为了弥补这个缺憾 在 TS 加入了枚举类型
  • 什么是枚举呢 ?
  • 枚举( mei ju ) : 枚举的意思就是一一列举,把所有情况都列举出来,那么取值的时候,只有这几个可以使用,其他的都不行
  • 计算机语言里面的枚举( enumerations ) : 把所有的常量放在一个集合内,让若干个常量变成一组有关联的内容

image.png

// 针对一个业务逻辑,我需要频繁用到四个方向的字符串
const UP = 'up'
const RIGHT = 'right'
const DOWN = 'down'
const LEFT = 'left'
  • 对于以上四个变量来说
  • 我不管做任何逻辑,我没办法限制你只能使用这四个变量中的一个
// 封装一个功能函数
function util(dir) {}
  • 不管用什么方法,你都没办法限制这个 dir 参数接收到的必须是上面列出的四个方向
  • 这个时候,我们就可以用到枚举了
  • 首先,在 TS 中,利用 enum 关键字创建一个枚举集合,把我们需要的四个常量放进去
enum Direction {
  UP = 'up',
  RIGHT = 'right',
  DOWN = 'down',
  LEFT = 'left'
}
  • 制作了一个 DIrection 枚举集合,那么就可以用这个集合来对某些数据进行限制了
function util(dir: Direction) {}
  • 这就约定了,dir 这个参数的值只能是 Direction 这个枚举集合里面的常量,其他都不行

image.png

  • 只要你写的不是 Direction 这个枚举内的内容都不行

数字枚举

  • 数字枚举 : 枚举类型中的每一个常量都是数字
  • 在 TS 中,枚举内的每一个常量,当你不设置值的时候,默认就是 number 类型
enum Pages {
  ONE,    // 0
  TWO,    // 1
  THREE   // 2
}
  • 你在枚举内的常量,第一个默认值是 0,后面的依次 +1 递
  • 此时
    • Pages.ONE => 0
    • Pages.TWO => 1
    • Pages.THREE => 2
  • 我们也可以自己指定值
enum Pages {
  ONE = 10,    // 10
  TWO = 20,    // 20
  THREE = 30   // 30
}
  • 这个时候枚举集合内的常量就是我们指定好的值
  • 我们也可以指定部分值
enum Pages {
  ONE = 10,    // 10
  TWO,         // 11
  THREE        // 12
}
  • 指定常量后面的未指定常量,就会按照 +1 的规则一次递增
enum Pages {
  ONE,         // 0
  TWO = 10,    // 10
  THREE        // 11
}
enum Pages {
  ONE,         // 0
  TWO = 10,    // 10
  THREE,       // 11
  FOUR = 30,   // 30
  FIVE         // 31
}

字符串枚举

  • 字符串枚举:枚举集合中的每一个常量的值都是 string 类型
  • 在 TS 内,你必须要指定一个值,才可能会出现 string 类型
enum Direction {
  UP = 'up',
  RIGHT = 'right',
  DOWN = 'down',
  LEFT = 'left'
}
  • 在 TS 中,枚举常量和任何内容都是不一样的,包括原始字符串
function util(dir: Direction) {}

image.png

  • 这是因为,在 TS 中,枚举内的每一个常量都是一个独一无二的值
  • 所以当你用枚举去限定一个数据的时候,用的时候也只能用枚举内的值
  • 这样也避免你因为手误出现的单词错误,比如你会不会认为 ‘form’ 和 ‘from’ 是一个单词呢

异构枚举

  • 异构枚举 : 其实就是在一个枚举集合内同时混合了数字枚举和字符串枚举
  • 但是你大概率是不会这样使用的,因为我们作为一组数据的集合,一般不会把数字和字符串混合在一起使用
enum Info {
  ONE,
  UP = 'up',
  TWO = 2,
  LEFT = 'left'
}
  • 在这里有一个点需要注意
    • 因为在枚举集合内,当某一个 key 你没有设置值的时候,会默认按照上一个的值 +1
    • 所以如果前一个是 字符串枚举,那么下一个必须要手动赋值,不然会报错
    • 如果前一个是 数字枚举,那么下一个可以不必要手动赋值,会按照上一个 +1 计算

枚举合并

  • 在 TS 内的枚举,是支持合并的
  • 多个枚举类型可以分开书写,会在编译的时候自动合并
enum Direction {
  UP = 'up',
  RIGHT = 'right',
  DOWN = 'down',
  LEFT = 'left'
}

enum Direction {
  TOP = 'top',
  BOTTOM = 'bottom'
}

function util(dir: Direction) {}

util(Direction.BOTTOM)
util(Direction.LEFT)
  • 这里定义的两个枚举都叫做 Direction,会在编译的时候自动放在一起,不会出现冲突

反向映射

  • TS 内的数字枚举,在编译的时候,会同时将 key 和 value 分别颠倒编译一次
enum Pages {
  ONE,    // 0
  TWO,    // 1
  THREE   // 2
}
  • 以这个为例,他是如何进行编译的呢
var Pages;
(function (Pages) {
  Pages[Enum["ONE"] = 0] = "ONE"
  Pages[Enum["TWO"] = 1] = "TWO"
  Pages[Enum["THREE"] = 2] = "THREE"
})(Pages || (Pages = {}));
  • 编译完毕的结果
Pages = {
  ONE: 0,
  TWO: 1,
  THREE: 2,
  '0': 'ONE',
  '1': 'TWO',
  '2': 'THREE'
}
  • 也就是说,我们在 TS 内使用的时候,如果是数字枚举
  • 那么我们可以通过 key 得到对应的数字,也可以通过对应的数字得到对应的 key
enum Pages {
  ONE,    // 0
  TWO,    // 1
  THREE   // 2
}

console.log(Pages.ONE)    // 0
console.log(Pages.TWO)    // 1
console.log(Pages.THREE)  // 2
console.log(Pages[0])     // 'ONE'
console.log(Pages[1])     // 'TWO'
console.log(Pages[2])     // 'THREE'

常量枚举

  • 常量枚举,是在枚举的基础上再加上 const 关键字来修饰
  • 会在编译的时候,把枚举内容删除,只保留编译结果
  • 并且对于数字枚举来说,不在支持反向映射能力,只能利用 key 来访问
  • 非常量枚举
enum Pages {
  ONE,    // 0
  TWO,    // 1
  THREE   // 2
}

console.log(Pages.ONE)
console.log(Pages.TWO)
console.log(Pages.THREE)
  • 编译完毕的 js 文件

image.png

  • 常量枚举
const enum Pages {
  ONE,    // 0
  TWO,    // 1
  THREE   // 2
}

console.log(Pages.ONE)
console.log(Pages.TWO)
console.log(Pages.THREE)
  • 编译完毕的 js 文件

image.png

二、类型约束

  • 在 TS 中,还有一个很神奇的关键字,叫做 type
  • type 又叫做类型别名有很多神奇的功能,不仅能支持 interface 定义的对象结构,还支持任何手写类型
  • 先来看一个很简单的例子

image.png

let n1: number | string | boolean
let n2: number | string | boolean
let n3: number | string | boolean
  • 观察上面一段代码,我们定义了 n1 和 n2 和 n3 三个变量
    • 对于类型的限制都是 number 或者 string 或者 boolean
  • 写起来的时候就非常麻烦
  • 这个时候,我们就可以使用 type 对其进行别名设置
type Info = number | string | boolean
let n1: Info
let n2: Info
let n3: Info
  • 这样一来,我们的代码是不是变得简洁了起来
  • 可能小伙伴们认为这个用的并不多,但是 type 也不是只有这一个功能

type 的常见使用

  • 基本类型的别名
type n = number
let num: n = 100
  • 这是一个非常基础的使用,把 number 这个类型起了一个别名叫做 n
  • 今后再用 n 来限制变量的时候,其实就是在使用 number
  • 基本类型联合
type i = number | string
let str: i = '千锋大前端'
str = 100
  • 这就是联合类型,那 number 或者 string 这个类型齐了一个别名叫做 i
  • 我们再用 i 来限制变量的时候,这个变量就被限制为了 number 或者 string
  • 对象类型
type User = { name: string, age: number }
let person: User = { name: '千锋大前端', age: 10 }
  • 这就是对象类型,和 interface 很像,用处基本一致
  • 对象联合类型
type User = { name: string, age: number }
type Person = User & { gender: boolean }
let person: Person = { name: '千锋大前端', age: 10, gender: true }
  • 这就是对象联合类型,和 interface 的 extends 继承很像
  • 元组类型
type data = [ number, string ]
let info: data = [ 10, '千锋大前端' ]
  • 函数类型
type func = (x: number, y: number) => number
  • 常量限定
type color = 'yellow' | 'orange' | 'blue'
function util(c: color) {}
util('yellow')
  • 这个 color 被限定为了几个值,将来用 color 去约束一个变量的时候
  • 这个变量只能接受这几个值,这里和 enum 比较像了

type 和 interface 的共同点

  1. 都可以约束 对象 或者 函数 类型
    • interface
interface User { name: string; age: number }
interface Func { (x: number): number }
  • type
type User = { name: string; age: number }
type Func = (x: number) => number
  • 我们看到,两个定义方式略有区别,但是后期用法基本一致
  1. 扩展类型
    • interface 使用 extends 进行继承
interface Person {
  name: string
  age: number
}

// 使用 extends 关键字继承自 Person
interface Student extends Person {
  classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }
  • type 使用 交叉(&) 来实现
type Person = {
  name: string
  age: number
}

// 使用 交叉(&) 
type Student = Person & {  
  classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }
  1. 联合类型
    • interface 使用 extends 继承 type
type Person = {
  name: string
  age: number
}

// 使用 extends 关键字继承自 Person
interface Student extends Person {
  classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }
  • type 使用 交叉(&) 扩展 interface
interface Person {
  name: string
  age: number
}

// 使用 交叉(&) 
type Student = Person & {  
  classRoom: number
}
let s: Student = { name: '千锋大前端', age: 10, classRoom: 1 }

type 和 interface 的区别

  1. interface 支持多次声明自动合并,type 不支持
interface User {
  name: string
  age: number
}
interface User {
  classRoom: string
}
/*
    真实的 User 接口
    {
        name: string
        age: number
        classRoom: string
    }
*/
  • type 如果声明重名标识符会报错

image.png

  1. 对于 ES6 模块化语法的默认导出语法
    • interface 支持声明的同时进行默认导出
export default interface User {  
  name: string
  age: number
}
  • type 必须先声明,在默认导出
type User = {
  name: string
  age: number
}
export default User
  • 必须要先声明好,在进行默认导出,如果直接连写默认导出,会报错

image.png

  1. type 可以使用 typeof 关键字去获取某一数据类型
let box = document.querySelector('.box')
type EleType = typeof box
  • 这里定义了一个 EleType 标识符,会自动根据 typeof 关键字检测的 box 的类型限制
  1. type 支持使用 in 关键字去遍历成映射类型
type names = 'firstName' | 'lastName' | 'AKA'
type nameType = {
  [key in names]: string
}
/*
    真实的 nameType 类型
    {
        firstName: string
        lastName: string
        AKA: string
    }
*/
<think>好的,我现在要回答用户关于JavaScriptTypeScript区别的问题。首先,我需要确认用户已经对前端后端有基本了解,因为之前的对话是关于这个的。接下来,用户可能在学习或工作中遇到了这两种语言,想了解它们的差异,以便选择合适的技术栈或者提升技能。 用户的问题比较直接,但需要详细对比两者的区别,同时可能需要知道适用场景。我需要从多个角度展开,比如类型系统、语法特性、工具支持、适用项目规模等方面。同时,要避免使用过于专业的术语,保持解释的易懂性。 首先,JavaScript是动态弱类型语言,而TypeScript是静态强类型,这是核心区别。需要解释静态类型检查的好处,比如错误检测提前、代码提示更智能等。然后,TypeScript作为JS的超集,意味着所有JS代码都是合法的TS代码,但TS增加了类型注解其他特性。 接着,可能需要提到工具链的支持,比如TS需要编译成JS才能运行,但现代开发流程中这通常不是问题,因为构建工具可以自动处理。另外,TS的接口、泛型、枚举等特性,对于大型项目维护更有帮助。 还要考虑用户可能的实际应用场景,比如小型项目可能不需要TS,但大型团队协作时TS的优势明显。同时,TS的学习曲线稍高,但长远来看能提高代码质量。 另外,用户可能想知道如何选择两者,所以需要给出建议,比如项目规模、团队经验等因素。最后,总结两者关系,强调TS并非替代JS,而是增强工具。 需要检查是否有遗漏点,比如社区支持、流行框架的支持情况(如React、Vue对TS的整合),以及编译时的类型检查具体如何工作。确保回答结构清晰,分点明确,便于用户理解。 可能还需要举一些简单的代码示例,比如变量类型声明,函数参数类型检查等,但用户没有明确要求代码,所以可能用文字描述更合适。同时,注意使用之前提到的格式要求,比如行内公式用$...$,独立公式用$$...$$,但在这个问题中可能涉及代码块,不过用户要求的是中文回答,可能不需要代码块,用文字说明即可。 最后,确保回答符合用户可见层的指导方针,结构清晰,逐步解决问题,真实可靠。检查是否有技术错误,比如TS是JS的超集,确实如此,而类型系统是静态的,这些都需要准确表达。</think>以下是对 JavaScriptTypeScript 的系统化对比分析,通过六个维度展现核心差异: --- ### 一、本质定位差异 1. **JavaScript (JS)** - **动态弱类型**脚本语言 - 浏览器原生支持的 Web 标准(ECMAScript 规范) - 直接解释执行,无需编译 2. **TypeScript (TS)** - **JavaScript 的超集**(所有 JS 代码都是合法 TS 代码) - 添加**静态类型系统**高级语法特性 - 需编译为 JS 后执行(通过 `tsc` 编译器或 Babel) --- ### 二、类型系统对比 | 特性 | JavaScript | TypeScript | |--------------------|-----------------------------|-------------------------------| | 变量类型声明 | 无强制类型声明(动态推断) | 支持显式类型注解(如 `let age: number = 25`) | | 类型检查时机 | 运行时可能暴露类型错误 | **编译时**提前发现类型错误 | | 类型灵活性 | 允许变量类型动态变化(如 `let x = 1; x = "text"`) | 默认禁止隐式类型转换,需通过联合类型(`let x: number | string`)实现 | **示例代码对比**: ```javascript // JavaScript(运行时可能出错) function add(a, b) { return a + b } add(2, "3") // 返回 "23"(非预期结果) // TypeScript(编译时直接报错) function add(a: number, b: number): number { return a + b } add(2, "3") // ❌ 编译错误:Argument of type 'string' is not assignable to parameter of type 'number' ``` --- ### 三、核心特性差异 1. **TypeScript 独有功能** - **接口与泛型**:定义数据结构约束(如 `interface User { id: number; name: string }`) - **枚举类型**:`enum Direction { Up, Down }` - **装饰器**:`@Component({...})`(广泛用于 Angular 框架) - **高级类型工具**:联合类型、交叉类型类型守卫等 2. **JavaScript 渐进增强** - ES6+ 特性(类、箭头函数、Promise)两者均支持 - TS 通过类型注解扩展 JS 功能,不改变运行时行为 --- ### 四、开发体验对比 | 场景 | JavaScript | TypeScript | |--------------------|--------------------------|---------------------------| | 代码提示 | 基础智能提示(依赖 IDE 推断) | **精准类型提示**(基于类型定义) | | 重构安全性 | 容易因类型不明确导致错误 | 支持安全的重命名、方法签名修改 | | 第三方库支持 | 原生支持所有 JS 库 | 需类型声明文件(如 `@types/react`)| --- ### 五、适用场景建议 1. **推荐 TypeScript** - 大型项目(代码量超 1 万行) - 团队协作开发(减少沟通成本) - 长期维护项目(类型即文档) - 框架开发(如 Angular、Vue 3 已用 TS 重构) 2. **可用原生 JavaScript** - 小型脚本或快速原型开发 - 已有 JS 代码库的简单扩展 - 对构建工具链有严格限制的场景 --- ### 六、技术生态关系 $$ \text{TypeScript} \xrightarrow{\text{编译}} \text{JavaScript} \xrightarrow{\text{执行}} \text{浏览器/Node.js} $$ - **工具链依赖**:TS 需要编译器,但现代工具(如 Webpack/Vite)可自动集成 - **类型定义文件**:DefinitelyTyped 社区提供主流库的 `.d.ts` 类型声明 - **框架支持**:React/Vue 均提供官方 TS 支持,Angular 默认使用 TS --- ### 总结选择策略 1. **学习路径**:建议先掌握 JavaScript 基础,再过渡到 TypeScript 2. **项目迁移**:可通过逐步添加 `.ts` 文件实现 JS 项目向 TS 渐进迁移 3. **趋势观察**:2023 年 Stack Overflow 调查显示,TS 已成为最受开发者欢迎的语言之一(73% 的喜爱率) 两者本质是**互补关系**而非替代关系:TypeScript 通过静态类型系统提升 JavaScript 的可维护性,最终仍编译为标准 JavaScript 运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨树林er

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值