TS扩展类型(类型别名、枚举、接口、类)

148 阅读5分钟

扩展类型:类型别名、枚举、接口、类

枚举

枚举通常用于约束某个变量的取值范围。

字面量和联合类型配合使用,也可以达到同样的目标。

字面量类型的问题

  • 在类型约束位置,会产生重复代码。可以使用类型别名解决该问题。
  • 逻辑含义和真实的值产生了混淆,会导致当修改真实值的时候,产生大量的修改。
  • 字面量类型不会进入到编译结果。

如何定义一个枚举:

enum 枚举名{
    枚举字段1 = 值1,
    枚举字段2 = 值2,
    ...
}
enum Level {
    level1,
    level2,
    level3
}

let l: Level = Level.level1;
l = Level.level2;

枚举会出现在编译结果中,编译结果中表现为对象。

image.png

枚举的规则:

  • 枚举的字段值可以是字符串或数字
  • 数字枚举的值会自动自增
  • 被数字枚举约束的变量,可以直接赋值为数字
  • 数字枚举的编译结果 和 字符串枚举有差异

最佳实践:

  • 尽量不要在一个枚举中既出现字符串字段,又出现数字字段
  • 使用枚举时,尽量使用枚举字段的名称,而不使用真实的值

扩展知识:位枚举(枚举的位运算)

针对的数字枚举

位运算:两个数字换算成二进制后进行的运算

比如我们在做后台管理项目中,会进行权限操作,完全就可以使用枚举的位运算来解决:

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文档,弱标准
  • 代码约束,强标准

和类型别名一样,接口,不出现在编译结果中

  1. 接口约束对象

  2. 接口约束函数

接口可以继承

可以通过接口之间的继承,实现多种接口的组合

使用类型别名可以实现类似的组合效果,需要通过&,它叫做交叉类型

它们的区别:

  • 子接口不能覆盖父接口的成员
  • 交叉类型会把相同成员的类型进行交叉
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

属性的初始化位置:

  1. 构造函数中
  2. 属性默认值

属性可以修饰为可选的

属性可以修饰为只读的

使用访问修饰符

访问修饰符可以控制类中的某个成员的访问权限

  • 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

属性简写

如果某个属性,通过构造函数的参数传递,并且不做任何处理的赋值给该属性。可以进行简写

访问器

作用:用于控制属性的读取和赋值