blog

类型声明以及组合用法

上一篇仅仅是描述了一些基础类型的使用,但是在我们开发时,数据的结构就会很复杂。

这样的话,上一篇的知识好像就不太够用了

下面我们就主要说一下在比较复杂的数据时,我们改如何组合类型

自定义类型的 关键字

interface、type、enum

type

基础用法

type Names = string[];

const names: Names = ['shange', 'liu'];

type 如果声明变量的 var、let、const一样,声明一个存储类型的变量,这个变量就代表赋予它的类型

但要注意的是 type 声明的类型不能更改,在同一作用域下(js 作用域)不能再次使用 type 声明同名称的类型

我们在来介绍两种常在使用 type 时使用的其他两个 ts 关键字 &|,这两个关键字可以使类型拆分、复用起来

& 符号的作用

咱要用到的类型,先声明

// 组成人体结构的类型

type Foot = {
    /** 左脚走路 */
    leftWalk: () => void;
    /** 右脚走路 */
    rightWalk: () => void;
};

type Head = {
    /** 嘴 */
    mouth: {
        /** 说话 */
        say(content: string): void;
    };
};

正常情况下,我们是这样写的

/** 以对象的结构聚合 */
type PersonStructure = {
    head: Head;
    foot: Foot;
    // 其他部位...
};

但我们为了举栗子,换个写法 (只是为了举栗子)

// 用 & 符号聚合
type PersonStructure = {
    head: Head;
} & { foot: Foot };

// 赋值给变量时聚合
const person: { head: Head } & { foot: FooT } = {
    head: xxx,
    foot: xxx,
};

作用

& 的作用就是将一个类型合并到另外一个类型之中,如同 js 中的 Objact.assign,将所有的对象聚合在一起后返回

** 符号的作用**
/** 普通的蚂蚁 */
type Ant = {
    // ... 各种基础类型
};

/** 飞蚁 */
type FlyingAnt = {
    name: 'fly';
    fly(): void;
} & Ant;

/** 工蚁 */
type WorkerAnt = {
    name: 'worker';
    move(): void;
} & Ant;

/** 注意,这里使用 | 来枚举变量可能存储值的类型 */
let flyingAnt1: WorkerAnt | FlyingAnt | undefined;

/** 可以整和成一个类型,还是枚举每个类型 */
type MenuAnt = WorkerAnt | FlyingAnt;
let flyingAnt1: MenuAnt | undefined;

// 用一个特定唯一的关键字来确定类型
if (flyingAnt1?.name === 'fly') {
    // type: FlyingAnt
    flyingAnt1.fly();
}

作用

| 的作用就是枚举这个变量要用到的所有类型,在后续的给变量赋值时,值必须在枚举的列表之中,否则的话,它就会报错

interface

基础用法

interface 常用于声明一个对象形式自定义类型,我们还可以使用 interface 来声明一个可以约束函数属性或类 的自定义类型

声明一个普通自定义类型

interface Person {
    name: string;
    age: number;
}

const person: Person = {
    name: 'shange',
    age: 18,
};

声明一个用于约束函数的类型

interface Say {
    (content: string): void;
}

const say: Say = content => void 0;

自定义属性的函数

// 还可以在给函数添加一些属性
interface Say {
    (content: string): void;
    // 要注意,isSay在初始化时一定为undefined,所以这里要用 ?: 或 | undefined 来声明可能为undefined的情况
    isSay?: boolean;
    // isSay: boolean | undefined;
}
const say: Say = content => void 0;

say.isSay = false;

拥有 this 作用域的函数

interface Say {
    // 函数的第一位参数作为this关键字,在解析函数参数时,会自动裁掉
    (this: Window, content: string): void;
    isSay?: boolean;
}

const say: Say = function () {
    this.name;
};

say.isSay = false;
const sayFn = say.bind(window);

interface 使用 extends 揉和多个对象类型 这里我们直接借用 type& 的基础类型,然后用 interface 可用的关键字来揉和类型

// 组成人体结构的类型

type Foot = {
    /** 左脚走路 */
    leftWalk: () => void;
    /** 右脚走路 */
    rightWalk: () => void;
};

type Head = {
    /** 嘴 */
    mouth: {
        /** 说话 */
        say(content: string): void;
    };
};

// 聚合在一起
interface PersonStructure {
    foot: Foot;
    head: Head;
}

// 继承Structure中的人体结构,extends 的作用和 & 是一样的
interface Person extends PersonStructure {
    name: string;
    age: number;
}

// extends 还可以继承多个类型,用 `,` 号隔开即可
interface Person extends PersonStructure, A, B, C {}

enum

通常,我们谈起 ts 时,必须了解一个特点,就是 ts 类型只存在与编译前,当转换为 js 时,根据类型来计算代码是否有错误,但转换为 js 后,js 不存在源码中写的类型,但就是有这样的一个 API,它参与了 js 运行时,就是这个 enum

接下来我们先了解下这个 API 的特性

同上,举个栗子

当我们用到颜色时,这个颜色可能还会在多个地方用到,但是又不想重复的写,我们就需要拿一个变量来替代

enum ColorMap {
    red = '#ff0000',
    blue = '#0000ff',
    green = '#00ff00',
    // ...
}

// 使用时
el.style.color = ColorMap.red;

enum 会产出一个类型,这个类型所包含的意思就是,这个被赋予这个类型的变量可能是枚举中的某一个

let color: ColorMap = ColorMap.red;
switch (color) {
    case ColorMap.red: {
        console.log('red');
        break;
    }
    case ColorMap.blue: {
        console.log('blue');
        break;
    }
}

不仅仅是上面的好处

当我们想要改将red对于的#ff0000从 16 进制改 rgb 时,我们可以直接改变对于的 value 即可

记住 enum 开发少使用,毕竟会参与运行时,也可以对象来替代

有兴趣可以看以下 ColorMap 在编译成 js 后有什么变化

结束

interfacetype 大家都需要掌握,enum 知道即可

interfacetype 都是组合类型和声明一个数据结构的必须要用的变量,那个方便就那个