扩展类型-枚举
枚举
定义:枚举是扩展类型中的一种,枚举通常用于约束某个变量的取值范围。
字面量和联合类型配合使用,也可以达到相同的目的。
扩展类型:类型别名、枚举、接口、类
字面量类型的问题
- 在类型约束位置,会产生重复代码;
let gender: "男" | "女";
gender = "男";
gender = "女";
function user (g: "男" | "女) {};
可以使用类型别名解决问题
type Gender = "男" | "女" ; // 将 “男” | “女” 抽离成类型别名,避免代码重复
let gender: Gender;
gender = "男";
gender = "女";
function user (g: Gender) {};
- 逻辑含义和真实的值产生了混淆,会导致当修改真实值的时候,产生大量的修改。(无法用类型别名解决);
- 字面量类型不会进入到编译结果;
枚举可以完美的解决以上的问题。
如何定义一个枚举
enum 枚举名{
枚举字段1 = 值1,
枚举字段2 = 值2,
...
}
enum Gender{
male = "男", // male是逻辑名称,“男”是真实的值,也可能是先生,帅哥
female = "女", // female是逻辑名称,“女”是真实的值,也可能是女士、美女
}
let gender: Gender;
gender = Gender.male; // 赋值时赋的是逻辑名称
gender = Gender.female;
枚举会参与编译,会在编译结果中出现,在编译结果中表现为对象
var Gender;
(function (Gender) {
Gender["male"] = "\u7537";
Gender["female"] = "\u5973";
})(Gender || (Gender = {}));
let gender;
gender = Gender.male;
gender = Gender.female;
枚举的规则
- 枚举的字段值可能是字符串或数字
- 数字枚举的值会自动递增
- 被数字枚举约束的变量,可以直接赋值为数字
企业开发经验
- 尽量不要在一个枚举中既出现字符串字段,又出现数字字段
- 使用枚举时,尽量使用枚举字段的名称,而不使用真实的值
demo:使用枚举优化扑克牌
// 枚举改写扑克牌
type Deck = Card[];
type Card = {
color: Color,
mark: Mark,
}
enum Color {
heart = "♥",
spade = "♠",
club = "♣",
diamond = "♦",
}
enum Mark {
A = "A",
two = "2",
three = "3",
four = "4",
five = "5",
six = "6",
seven = "7",
eight = "8",
nine = "9",
ten = "10",
eleven = "J",
twelve = "Q",
thirteen = "K",
}
function createDeck (): Deck {
const deck : Deck = [];
const marks = Object.values(Mark);
const colors = Object.values(Color);
for (const m of marks) {
for (const c of colors ) {
deck.push({
color: c,
mark: m
})
}
}
return deck;
}
function printDeck(deck: Deck) {
let result = '\r';
deck.forEach((card, i) => {
let str = card.color + card.mark;
result += str + '\t';
if ((i+1)%6 === 0) {
result += '\n';
}
})
console.log(result);
}
const deck = createDeck();
printDeck(deck);
打印出来的结果
扩展知识-枚举的位运算
一个文件有4种权限,读、写、删、创建
写成枚举
enum Permission {
Read = 1, // 0001
Write = 2, // 0010
Create = 4, // 0100
Delete = 8 // 1000
}
- 如何组合权限 或运算:参加运算的两个数据,按二进制位进行“与”运算,有一位是1就得1;
let p: Permission = Permission.Read | Permission.Write;
- 如何判断是否拥有某个权限 与运算:参加运算的两个数据,按二进制位进行“与”运算,全部为1才得1;
function hasPermission(target: Permission, per: Permission): boolean {
return (target & per) === per;
}
// 例:判断p是否有可读的权限
hasPermission(p, Permission.Read);
- 如何删除某个权限 // 异或运算:参加运算的两个数据,按二进制位进行“与”运算,相同取零,不同取一;
p = p ^ Permission.Write;
模块化
关于模块化的相关配置:
| 配置名称 | 含义 |
|---|---|
| module | 设置编译结果中使用的模块化标准 |
| moduleResolution | 设置解析模块的模式 |
| noImplicitUseStrict | 编译结果中不包含"use strict" |
| removeComments | 编译结果移除注释 |
| noEmitOnError | 错误时不生成编译结果 |
| esModuleInterop | 启用es模块化交互非es模块导出 |
TS中如何书写模块化语句
最佳实践:TS中,导入和导出模块,统一使用ES6的模块化标准
不使用ES6模块化标准(不推荐)
# 编译结果中的模块化
可以在tsconfig.json文件配置
TS中的模块化在编译结果中:
- 如果编译结果的模块化标准是ES6: 没有区别
- 如果编译结果的模块化标准是commonjs:导出的声明会变成exports的属性,默认的导出会变成exports的default属性;
# 如何在TS中书写commonjs模块化代码
导出:export = xxx
导入:import xxx = require("xxx")
# 模块解析
模块解析:应该从什么位置寻找模块
TS中,有两种模块解析策略
- classic:经典
- node:node解析策略(唯一的变化,是将js替换为ts)
- 相对路径```require("./xxx")```
- 非相对模块```require("xxx")```
接口
接口:inteface
扩展类型:类型别名、枚举、接口、类
TypeScript的接口:用于约束类、对象、函数的契约(标准)
契约(标准)的形式:
- API文档,弱标准
- 代码约束,强标准
和类型别名一样,接口,不出现在编译结果中
接口约束对象
接口约束函数
类型兼容性
B->A,如果能完成赋值,则B和A类型兼容
鸭子辨型法(子结构辨型法):目标类型需要某一些特征,赋值的类型只要能满足该特征即可
基本类型:完全匹配
对象类型:鸭子辨型法
类型断言
当直接使用对象字面量赋值的时候,会进行更加严格的判断
- 函数类型
参数:传递给目标函数的参数可以少,但不可以多
返回值:要求返回必须返回;不要求返回,你随意;
TS中的类
面向对象思想
基础部分,学习类的时候,仅讨论新增的语法部分。
属性
使用属性列表来描述类中的属性
属性的初始化检查
strictPropertyInitialization:true
属性的初始化位置:
- 构造函数中
- 属性默认值
属性可以修饰为可选的
属性可以修饰为只读的
使用访问修饰符
访问修饰符可以控制类中的某个成员的访问权限
- public:默认的访问修饰符,公开的,所有的代码均可访问
- private:私有的,只有在类中可以访问
- protected:暂时不讲
Symble
属性简写
如果某个属性,通过构造函数的参数传递,并且不做任何处理的赋值给该属性。可以进行简写
访问器
作用:用于控制属性的读取和赋值
泛型
有时,书写某个函数时,会丢失一些类型信息(多个位置的类型应该保持一致或有关联的信息)
泛型:是指附属于函数、类、接口、类型别名之上的类型
泛型相当于是一个类型变量,在定义时,无法预先知道具体的类型,可以用该变量来代替,只有到调用时,才能确定它的类型
很多时候,TS会智能的根据传递的参数,推导出泛型的具体类型
如果无法完成推导,并且又没有传递具体的类型,默认为空对象
泛型可以设置默认值
在函数中使用泛型
在函数名之后写上<泛型名称>
如何在类型别名、接口、类中使用泛型
直接在名称后写上<泛型名称>
泛型约束
泛型约束,用于现实泛型的取值
多泛型
深入理解类和接口
面向对象概述
为什么要讲面向对象
- TS为前端面向对象开发带来了契机
JS语言没有类型检查,如果使用面向对象的方式开发,会产生大量的接口,而大量的接口会导致调用复杂度剧增,这种复杂度必须通过严格的类型检查来避免错误,尽管可以使用注释或文档或记忆力,但是它们没有强约束力。
TS带来了完整的类型系统,因此开发复杂程序时,无论接口数量有多少,都可以获得完整的类型检查,并且这种检查是据有强约束力的。
- 面向对象中有许多非常成熟的模式,能处理复杂问题
在过去的很多年中,在大型应用或复杂领域,面向对象已经积累了非常多的经验。
什么是面向对象
面向对象:Oriented(基于) Object(事物),简称OO。
是一种编程思想,它提出一切以类对切入点思考问题。
其他编程思想:面向过程、函数式编程
学开发最重要最难的是什么?思维
面向过程:以功能流程为思考切入点,不太适合大型应用
函数式编程:以数学运算为思考切入点
面向对象:以划分类为思考切入点。类是最小的功能单元
类:可以产生对象的模板。
如何学习
- TS中的OOP (面向对象编程,Oriented Object Programing)
- 小游戏练习
理解 -> 想法 -> 实践 -> 理解 -> ....