扩展类型:类型别名、枚举、接口、类
枚举
枚举通常用于约束某个变量的取值范围。
字面量和联合类型配合使用,也可以达到同样的目标。
字面量类型的问题
- 在类型约束位置,会产生重复代码。可以使用类型别名解决该问题。
- 逻辑含义和真实的值产生了混淆,会导致当修改真实值的时候,产生大量的修改。
- 字面量类型不会进入到编译结果。
如何定义一个枚举:
enum 枚举名{
枚举字段1 = 值1,
枚举字段2 = 值2,
...
}
enum Level {
level1,
level2,
level3
}
let l: Level = Level.level1;
l = Level.level2;
枚举会出现在编译结果中,编译结果中表现为对象。
枚举的规则:
- 枚举的字段值可以是字符串或数字
- 数字枚举的值会自动自增
- 被数字枚举约束的变量,可以直接赋值为数字
- 数字枚举的编译结果 和 字符串枚举有差异
最佳实践:
- 尽量不要在一个枚举中既出现字符串字段,又出现数字字段
- 使用枚举时,尽量使用枚举字段的名称,而不使用真实的值
扩展知识:位枚举(枚举的位运算)
针对的数字枚举
位运算:两个数字换算成二进制后进行的运算
比如我们在做后台管理项目中,会进行权限操作,完全就可以使用枚举的位运算来解决:
enum Permission {
Read = 1, // 0001
Write = 2, // 0010
Create = 4, // 0100
Delete = 8 // 1000
}
//1. 如何组合权限
//使用或运算
//0001
//或
//0010
//0011
let p: Permission = Permission.Read | Permission.Write;
//2. 如何判断是否拥有某个权限
//0011
//且
//0010
//0010
function hasPermission(target: Permission, per: Permission) {
return (target & per) === per;
}
//判断变量p是否拥有可读权限
//3. 如何删除某个权限
//0011
//异或
//0010
//0001
p = p ^ Permission.Write;
console.log(hasPermission(p, Permission.Write));
接口和类型兼容性
扩展类型-接口
接口:inteface
TypeScript的接口:用于约束类、对象、函数的契约(标准)
契约(标准)的形式:
- API文档,弱标准
- 代码约束,强标准
和类型别名一样,接口,不出现在编译结果中
-
接口约束对象
-
接口约束函数
接口可以继承
可以通过接口之间的继承,实现多种接口的组合
使用类型别名可以实现类似的组合效果,需要通过&,它叫做交叉类型
它们的区别:
- 子接口不能覆盖父接口的成员
- 交叉类型会把相同成员的类型进行交叉
interface A {
T1: string
}
interface B {
T2: number
}
interface C extends A, B {
T3: boolean
}
readonly
只读修饰符,修饰的目标是只读
只读修饰符不在编译结果中
const arr: readonly number[] = [3, 4, 6];
const arr: ReadonlyArray<number> = [3, 4, 6];
类型兼容性
B->A,TS会进行类型检查,如果能完成赋值,则B和A类型兼容
鸭子辨型法(子结构辨型法):目标类型需要某一些特征,赋值的类型只要能满足该特征即可
-
基本类型:完全匹配
-
对象类型:鸭子辨型法
interface Duck {
sound: "嘎嘎嘎"
swin(): void
}
let person = {
name: "伪装成鸭子的人",
age: 11,
sound: "嘎嘎嘎" as "嘎嘎嘎",
swin() {
console.log(this.name + "正在游泳,并发出了" + this.sound + "的声音");
}
}
let duck: Duck = {
sound: "嘎嘎嘎" as "嘎嘎嘎",
swin() {
console.log(this.name + "正在游泳,并发出了" + this.sound + "的声音");
}
};
// person中有满足sound、swin特性,TS中只需判断他的结构是否满足即可
let duck: Duck = person
类型断言
当直接使用对象字面量赋值的时候,会进行更加严格的判断
- 函数类型
一切无比自然,和JS的写法无缝衔接
参数:传递给目标函数的参数可以少,但不可以多
返回值:要求返回必须返回;不要求返回,你随意;
type Condition = (n: number) => boolean
// or
interface Condition {
(n: number): boolean
}
function sum(numbers: number[], callBack: Condition) {
let s = 0;
numbers.forEach(n => {
if (callBack(n)) {
s += n;
}
})
return s;
}
const result = sum([3, 4, 5, 7, 11], n => n % 2 !== 0);
console.log(result);
TS中的类
面向对象思想
在基础部分,学习类的时候,仅讨论新增的语法部分。
属性
使用属性列表来描述类中的属性
属性的初始化检查
strictPropertyInitialization:true
属性的初始化位置:
- 构造函数中
- 属性默认值
属性可以修饰为可选的
属性可以修饰为只读的
使用访问修饰符
访问修饰符可以控制类中的某个成员的访问权限
- public:默认的访问修饰符,公开的,所有的代码均可访问
- private:私有的,只有在类中可以访问
- protected:暂时不讨论
class User {
readonly id: number //修饰为只读的
gender: "男" | "女" = "男"
pid?: string
private _publishNumber: number = 3; //每天一共可以发布多少篇文章
private _curNumber: number = 0; //当前可以发布的文章数量
constructor(public name: string, private _age: number) {
this.id = Math.random();
}
set age(value: number) {
if (value < 0) {
this._age = 0;
}
else if (value > 200) {
this._age = 200;
}
else {
this._age = value;
}
}
get age() {
return Math.floor(this._age);
}
publish(title: string) {
if (this._curNumber < this._publishNumber) {
console.log("发布一篇文章:" + title);
this._curNumber++;
}
else {
console.log("你今日发布的文章数量已达到上限");
}
}
}
const u = new User("aa", 22);
//c#
u.age = 1.5;
console.log(u.age);
u.publish("文章1")
u.publish("文章2")
u.publish("文章3")
u.publish("文章4")
u.publish("文章5")
u.publish("文章6")
Symble
属性简写
如果某个属性,通过构造函数的参数传递,并且不做任何处理的赋值给该属性。可以进行简写
访问器
作用:用于控制属性的读取和赋值