前端 - 笔记 -【TypeScript - 常见类型】-【基本、值、联和、交叉、数组、元组、函数、别名、接口、枚举、泛型、any、类型断言】

556 阅读19分钟

前言

类型注解

  • 概念
    • 变量 添加 类型约束,使变量 只能被赋值为约定好的类型,同时可以有相关的类型提示;
  • 作用
    • 限制变量能赋值的数据类型并给出提示;
  • 解释
    • 约定了什么类型,就只能给变量赋该类型的值(赋别的类型的值会报错);
  • 语法
    变量: 类型
    
  • 代码展示:
    // : number - 就是类型注解
    // 约束变量的类型 - 约束的是什么类型,就只能给这个变量赋该类型的值,否则会报错
    let age: number = 23
    age = 24
    // age = '24' - 报错:不能将类型"string",分配给类型"number"
    
  • 说明: : number 就是 类型注解,约束变量 age 只能被赋值为 number类型,同时可以有 number类型相关的提示;
    • image-20230209113048244

类型分类

  • 可以将TS中的常用类型细分为 两类
    • JS已有类型
    • TS新增类型

JS已有类型

  • 基本数据类型
    • numberstringbooleannullundefinedsymbol
    • 特点:简单,完全按照JS中类型的名称来书写;
  • symbol
    • 表示独一无二的值,最大的用法是用来定义对象的唯一属性名;
    • Symbol函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是函数。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分;
      const a: number = 1;
      const b: string = '1';
      const c: boolean = true;
      const d: undefined = undefined;
      const e: null = null;
      const f: symbol = Symbol('symbol');
      
  • 引用数据类型
    • object(数组、对象、函数、……);
    • 特点:对象类型,在TS中更加细化,每个具体的对象都有自己的类型语法
    // 数组类型的两种写法:(推荐第一种写法)
    
    // ✅ 数值类型数组 - 因为已经约束为number类型,所以在该数组中只能出现number类型,不能出现其他类型
    const arr: number[] = [1, 2];
    
    // ❌ 字符串类型数组 - 不推荐
    const arr1: Array<number> = [1, 2];
    
    const obj: {
        name: string
        age: number
        sayHi(): void
        // 参数可传可不传
        greet: (type?: string) => void
    } = {
        name: '奥特曼',
        age: 12,
        sayHi: () => {},
        greet: (type) => type
    };
    obj.greet();
    
    const timeObj: {
        startTime: number
        endTime: Date
    } = {
        startTime: new Date().getTime(),
        endTime: new Date()
    };
    

TS新增类型

  • 联合类型(|);
  • 自定义类型(类型别名)(type);
  • 接口(interface);
  • 元组(特殊的数组);
  • 字面量类型;
  • 枚举(enum);
  • void(空);
  • 泛型;
  • any(任何类型)等;

一、基本类型,类型名大小写区别?

  • 类型写法可看【前言-JS已有类型】;
  • 简单类型类型名 只能是 小写
    • image.png
  • 有时候发现,类型名首字母大写,也不会报错,但是所表达的意思就不一样了;
    • 小写
      • 表示 该变量 的 值 是 string 类型;
    • 大写
      • 表示 该变量 是一个 实例对象
    • 为什么大写的 String 也可以?
      • 像数字、字符串等,在 JS 内部有个 内部包装类 ,当我们把 字符串当对象使用的时候,在 js 内部会帮我们自动进行包装;
    // 类型名小写
    // 约束变量的str的类型是 string 类型
    let str: string = '13';
    
    // 类型名大写
    // 此处的意思是 str1 表示是个 实例对象,是 String 的 实例对象
    let str1: String = '123';
    // 这种才是 大写 String 的正确赋值方法
    let str2: String = new String('123');
    

二、值类型

TS规定,单个值也可以是一种类型,称为值类型;

let x: 'hello';

x = 'hello'; // 正确
x = 'world'; // 报错
// 变量 x 的类型是字符串 'hello',导致它只能赋值为这个字符串,赋值其他的字符串为报错;
  • TS推断类型时,遇到 const 命令声明的变量,如果没有显示注明类型,就会推断为该变量是值类型;
    // x 的类型是 'api'
    const x = 'api';
    
    // y 的类型是 string
    const y: string = 'api';
    
  • 注意
    • const 命令声明的变量,如果赋值为对象,并不会推断为值类型;

三、联合类型

联合类型(|):由 两个多个 其他类型 组成的 类型,表示可以是 这些类型 中的 任意一种

  • 代码展示:
    // 联合类型 - |
    // 添加小括号:首先是数组,其次,这个数组中元素的类型
    let arr: (number | string | boolean | object)[] = [1, true, 'a', {name: '哈哈'}]
    
    // ❗ 注意区分
    
    // 不添加小括号:test既可以是数字 也可以是 字符串型的数组
    let test: number | string[] = 1
    let test1: number | string[] = ['a']
    
  • 🔺 注意
    • 添加小括号
      • 可以是 许多类型 中的 任意一种
      • 也可是 多种类型 同时出现
    • 不添加小括号
      • const a = string | number[] = 1;
      • const a = string | number[] = ['1', '2'];
      • 既可以是 string类型,也可以是 number类型的数组;
  • 联合类型可以与值类型相结合,表示一个变量的值有若干种可能;

四、交叉类型

交叉类型:指的多个类型组成的一个新类型,使用符号 & 表示;

  • 交叉类型的主要用途是表示对象的合成;
// 对象 obj,必须同时具有 a、b 两个属性,并且对应的属性值要满足约定的类型
let obj: { a: number } & { b: string };
obj = {
    a: 1,
    b: '1',
};
  • 交叉类型 可以结合 类型别名 来实现 继承;

五、数组类型

5.1 添加类型

  • 数组 的 类型注解 有 两种方式:

5.1.1 ✅ 方括号

  • 在数组成员的类型后面,加上一对方括号;
    // 字符串类型的数组
    const arr: string[] = ['1'];
    
    // 数字类型的数组
    const arr1: number[] = [1];
    
    // 对象类型的数组
    const objArr: object[] = []
    

5.1.2 ❌ TS内置 Array 接口

  • 使用 TypeScript 内置的 Array 接口;
    // 数字类型的数组
    const arr; Array<number> = [1];
    
    // 字符串类型的数组
    const str: Array<string> = ['1'];
    

5.2 只读数组

  • js规定,使用 const 声明的数组变量是可以改变成员的;
  • 但是很多时候,确实有声明为只读数组的需求,即不允许变动数组成员;
  • TypeScript 允许声明只读数组,有两种方式:
    • 数组类型前面加上 readonly 关键字;
    • 使用 ReadonlyArray 接口;
// 只读数组
const arr: readonly number[] = [1, 2, 3];
const arr1: ReadonlyArray<number> = [1, 2, 3];

arr[0] = 2; // 报错
arr1[0] = 2; // 报错

image.png

5.3 多维数组

  • TypeScript 使用 T[][] 的形式,表示二维数组,T是最底层数组成员的类型;
// 多维数组
const arr11: number[][] = [[1, 2], [3, 4]];
const arr2: (number | string)[][] = [[1, 2], [2, '3']];

// 当然,也可以使用 内置接口去实现,这里就不展示了,大家感情兴趣的话,可以自行尝试哈

六、元组类型

元组是TS特有的数据类型,JS没有单独区分这种类型; 它表示:成员类型可以自由设置的数组,即数组的各个成员的类型可以不同; 元组类型是另一种类型的数组,它确切的知道 包含多少个元素,以及 每个元素的类型;

  • 元组必须明确声明每个成员的类型:
    const arr: [number, string, null, boolean] = [1, '1', null, true];
    
  • 元组类型的写法,与数组有一个差异,数组的成员类型是写在方括号外面的(number[]),元组的成员类型是写在方括号里面的([number])

七、函数类型

  • 函数类型:
    • 实际上指的是 函数 参数返回值 的类型;

7.1 添加类型

  • 为函数指定类型的有两种方式:
    1. 单独指定参数、返回值的类型
      // 单独指定参数和返回值类型 - 声明式函数 + 函数表达式
      function add0(a: number, b: number): number {
        return a + b;
      }
      // 设置参数默认值
      function add(a: number = 0, b: number = 0): number {
        return a + b;
      }
      
      const add1 = (a: number, b: number): number => a + b;
      // 设置参数默认值
      const add2 = (a: number = 1, b: number = 1): number => a + b;
      
    2. 同时指定参数、返回值的类型:(❗只适用于函数表达式
      // 同时给 参数 和 返回值 指定类型
      // 同时指定参数和返回值类型 - 只适用于 函数表达式
      const sub: (a: number, b: number) => number = (a, b) => a - b;
      // 设置参数默认值
      const sub1: (a: number, b: number) => number = (a = 0, b = 0) => a - b;
      
  • 🔺注意
    • 如果 函数 没有 返回值,那么,函数 返回值 类型为:void
    // 如果函数没有返回值,那么,函数返回值的类型为void
    function add(name: string): void {
        console.log(name);
    }
    add('小Q');
    

7.2 可选参数

  • 如果函数的某个参数可以省略,则在 参数名称 后面 添加 ?
const fn: (name?: string) => void = (name) => {
    console.log(name ?? '嘻嘻哈哈')
};
  • 注意
    • 可选参数 只能出现参数列表最后,也就是说可选参数后面不能再出现必选参数;

7.3 参数默认值

  • TS函数的参数默认值写法与JS一致;
  • 设置了默认值的刹那好苏,就是可选的,如果不传入该参数,它就会等于默认值;
const add: (a: number, b: number) => number = (a = 0, b = 0) => (a + b);

七、类型别名

7.1 基本使用

  • 类型别名
    • 自定义类型
    • 为任意类型起别名;
  • 使用场景
    • 当同一类型(比较复杂的类型)被多次使用时,可以通过类型别名,简化该类型的使用;

    解释

    1. 使用 type 关键字类创建类型别名;
    2. 类型别名可以是任意合法的变量名称;
    3. 创建类型别名后,直接 使用该类型别名作为变量的类型注解 即可;
  • 语法
    type 类型别名 = { 定义类型 };
    
  • 注意
    • 类型别名的命名采用规范的 大驼峰格式
    • 类型别名 不能 重复定义
  • 代码展示:
/**
 * 使用 type 关键字来创建类型别名
 * 类型别名可以是任意合法的变量名称
 * 创建类型别名后,直接 使用该类型别名作为变量的类型注解 即可
 */

type CustomArray = (number | string)[]
let arr1: CustomArray = [1, 2, 'a']
let arr2: CustomArray = ['x', 'y', 'z', 6, 7]

7.2 type + 交叉类型模拟继承

  • 类型别名 配合 交叉类型&)可以 模拟 继承,同样可以实现类型复用;
  • 代码展示:
    // 父接口
    type GoodsType = {
        id: string;
        price: number;
    }
    
    // 子接口继承
    type DIsGoodsType = GoodsType & {
        disPrice: number;
    }
    

八、接口

  • 当一个 对象类型多次使用 的时候,一般会使用 接口(interface) 来描述 对象类型,达到复用的目的;

8.1 基本使用

  • 作用
    • TS 中使用 interface 接口来描述对象的数据类型(常用于给对象的属性和方法添加类型约束);
  • 解决了什么问题:
    • 类型的复用问题;
  • 注意
    • 使用 interface 关键字来声明接口;
    • 接口名称,可以是任意合法的变量名称;
    • 声明接口后,直接使用 接口名称 作为变量的类型;
    • 因为每一行只有一个属性类型,因此,属性类型后面没有 ;
    • 一般很少使用 object,基本都是使用 interface 去定义对象,更加准确;
    • interface 接口类型实际限制的是 字段名字段的类型
    • interfacee 是可以重名的;
      • 第一个接口后面的接口就相当于给第一个接口增加类型;
      interface A {
          msg: string;
          status: number
      }
      
      interface A {
          code: number
      }
      
      // 现在的 A 就等于下面这样
      interafce A {
          msg: string;
          status: number;
          code: number
      }
      
  • 代码展示:
    // 定义接口
    interface IPerson {
        name: string
        age: number
        work: string
        city: string
        sayHi(): void
        greet: (str: string) => void
    }
    
    // 使用接口
    const obj: IPerson = {
        name: '邵秋华',
        age: 24,
        work: '中级Web前端开发工程师',
        city: '武汉',
        sayHi() {},
        greet(str) {}
    }
    
    console.log(obj);
    
  • 接口练习:
    • 下面 interface 中的 分号 可以 省略;
    // 后端返回到数据
    /* {
      message: [
        {
          image_src: "",
          open_type: "",
          goods_id: 122,
          navigator_url: ""
        }
      ],
      meta: {
        msg: "",
        status: 200
      }
    } */
    
    // 方式一 ✅:使用多个 interface
    interface BannerInfo {
        image_src: string;
        open_type: string;
        goods_id: number;
        navigator_url: string;
    }
    
    interface ResInfo {
        msg: string;
        status: number;
    }
    
    interface BannerRes {
        message: BannerInfo[];
        meta: ResInfo
    }
    
    // 方式二 ❌:纯手写
    interface BannerRes {
        message: Array<{
            image_src: string;
            open_type: string;
            goods_id: number;
            navigator_url: string;
        }>;
        meta: {
            msg: string;
            status: number
        }
    }
    
    interface BannerRes {
        message: {
            image_src: string;
            open_type: string;
            goods_id: number;
            navigator_url: string;
        }[];
        meta: {
            msg: string;
            status: number
        }
    }
    

8.2 接口的继承

  • 如果两个接口之间有相同的属性或方法,可以将公共的属性或方法抽出来,通过继承来实现复用;
  • 接口的继承使用过关键字 extends
  • 代码展示:
    interface Meta {
      msg: string;
      status: number;
    }
    
    // 接口 Res1 继承 Meta
    interface Res1 extends Meta {
      success: boolean
    }
    
    const xiHa: Res1 = {
      success: true,
      msg: "",
      status: 200
    }
    

解释

  • 使用 extends(继承) 关键字实现了接口 Res1 继承 Meta;
  • 继承后,Res1 就有了 Meta 的所有属性和方法;

8.3 接口的可选字段

  • 概念
    • 通过 ? 对属性进行可选标注,赋值的时候 该属性可以缺失,如果有值必须保证类型满足要求;
  • 代码展示:
    interface Res {
        msg: string;
        status: number;
        code: number;
        success?: boolean
    }
    
    const resInfo: Res = {
        msg: "",
        status: 200,
        code: 200
    }
    

8.4 接口和类型别名的对比

  • 相同点
    • 都可以 给 对象 指定类型
    • 都能实现继承的效果:
      • interface 使用 extends
      • type 配合 交叉类型 模拟 继承
  • 不同点
    • 接口
      • 只能 为 对象 指定类型
      • 同名的接口会合并(属性取并集,不能出现类型冲突);
    • 类型别名
      • 可以为 任意类型 指定 别名
      • 不能声明同名的 type(会报错);
  • image-20230209160209549

九、对象类型

9.1 基本使用

  • JS 中的 对象 是由 属性方法 构成 的;
  • TS对象类型 就是在 描述对象 结构(由什么类型的属性和方法);
  • 注意
    • 直接使用 {} 来描述对象结构;
      • 属性 采用 属性名:类型 的形式;
      • 方法 采用 方法名(): 返回值类型 的形式;
    • 如果 方法参数,就在 方法名 后面的 小括号指定参数类型(比如:greet(name: string): void);
    • 一行代码指定对象的多个属性类型 时,使用 ; 来分隔
      • 如果 一行代码只指定一个属性类型(通过换行来分隔多个属性类型),可以去掉 ;
      • 方法的类型 也可以使用 箭头函数形式
  • 对象类型的写法:
// ❌
let person: {name: string; age: number; sayHi(): void; greet?:(str: string) => void} = {
    name: '邵秋华',
    age: 23,
    sayHi() {},
    greet(str) {}
}

// 和上面的写法意义是一样的,更推荐下面这种,写起来方便很多
// ✅
let person: {
    name: string
    age: number
    sayHi(): void
    greet?:(str: string) => void
} = {
    name: '邵秋华',
    age: 23,
    sayHi() {},
    greet(str) {}
}

9.2 可选属性

  • 可选属性的语法与函数可选参数的语法一致,都是用 ? 来表示;
  • 代码展示:
    function myAxios(config: {url: string; method?: string}): void {
        console.log(config);
    }
    const myAxios1: (config: {url: string; method?: string}) => void = (config) => {
        console.log(config);
    }
    
    myAxios({
        url: '哈哈嘻嘻',
        method: 'POST'
    })
    
    myAxios1({
        url: '嘻嘻哈哈'
    })
    

9.3 只读属性

  • 属性名前面加上 readonly 关键字即可;
  • 注意
    • 只读属性只能在对象初始化期间赋值,此后就不能修改该属性了(修改就会报错);
    • 属性值是个基本数据类型,修改(替换)就会报错;
    • 属性值是个复杂数据类型,readonly 修饰符并不禁止修改该对象的属性,只是禁止完全替换掉该属性值;
interface TestObjType {
  readonly name: string;
  age: number;
  readonly likeArr: string[];
  readonly textOptions: {
    city: string;
    text: string;
  }
};

// 对象初始化
const testObj: TestObjType = {
  name: '嘻哈少将',
  age: 25,
  likeArr: ['打游戏', '看电影'],
  textOptions: {
    city: '北京',
    text: '哈哈哈'
  }
}

testObj.name = '张三'; // 报错
testObj.likeArr[0] = '躺尸';
testObj.textOptions.city = '深圳';
testObj.textOptions = {}; // 报错

image.png

十、枚举类型

  • 枚举的功能类似于 字面量类型 + 联合类型 组合的功能,也可以 表示一组明确的可选值

10.1 简介

  • 枚举
    • 定义一组 命名常量
    • 使用 enum 关键字定义枚举;
    • 他描述一个值,该值可以是这些命名常量中的一个;
enum Direction { Up, Down, Left, Right }
function changeDirection(direction: Direction) {
  console.log(direction);
}
  • 🔺 注意:
    • 使用 enum 关键字定义枚举;
    • 约定枚举名称、枚举中的值以 大写字母开头
    • 枚举中的多个值之间通过 ,(逗号)分隔;
    • 定义好枚举后,直接使用 枚举名称 作为 类型注解
  • 访问枚举成员
    • 类似于JS中的对象,可以通过 . / [] 语法访问枚举成员;

10.2 数字枚举

  • 枚举成员是有值的,默认为:从0开始递增的数值
  • 数字枚举:枚举成员 为 数字 的 枚举;
  • 当然,也可以给枚举中的成员初始化值;
    enum Direction {
      Up = 2,
      Down = 4,
      Left = 8,
      Right = 16
    }
    
    function changeDirection(direction: Direction) {
      console.log(direction);
    }
    
    // 访问 枚举成员
    changeDirection(Direction.Up);
    changeDirection(Direction.Down);
    changeDirection(Direction.Left);
    changeDirection(Direction.Right);
    
  • 枚举值也可以是不连续的;
    enum TestEnum {
        a = 1,
        b, // 2
        c, // 3
        d = 10,
        e, // 11
    }
    

10.3 字符串枚举

  • 字符串枚举枚举成员字符串
  • 🔺 注意
    • 字符串枚举没有自增长行为,因此,字符串枚举 的 每个成员 必须有 初始值
// 字符串枚举
enum Direction {
  Up = 'UP',
  Down = 'Down',
  Left = 'Left',
  Right = 'Right'
}

10.4 同名的枚举

  • 多个同名的Enum结构会自动合并;
enum Test { A, };
enum Test { B = 1, };
enum Test { C = 3, };
enum Test { D = 5, };

// 等同于
enum Test {
    A,
    B,
    C = 3,
    D = 5,
}
  • 注意
    • Enum结构合并时,只允许第一个声明 的成员省略初始值,否则会报错;
    • Enum结构合并时,不能有同名成员,否则会报错;
    • 同名Enum合并的另一个限制是,所有定义必须同为 const 枚举 或者 非const 枚举,不允许混合使用;
enum Test { A, };
enum Test { B, }; // 报错

// -------------

enum Test { A, B, };
enum test { B = 1, C = 2, }; // 报错:标识B重复

// -------------

// 正确
enum Test { A, };
enum Test { B = 1,};

// 正确
const enum Xi { A, };
const enum Xi { B = 1, };

// 报错
enum Ha { A, };
const enum Ha { B = 1, };

10.5 枚举的特点及原理

  • 枚举是TS为数不多的非JavaScript类型级扩展(不仅仅是类型)的特征之一
    • 其他类型仅仅被当作类型,而枚举不仅用做类型,还提供值(枚举成员都是有值的)
    • 也就是说,其他的类型都会在编译为JS代码时自动移除,但是,枚举类型会被编译为JS代码
    • image-20230210141641891
    • 说明:
      • 枚举与前面讲到的字面量类型+联合类型组合的功能类似,都用来表示一组明确的可选值列表
      • 一般情况下,推荐使用字面量类型+联合类型组合的方式,因为相比枚举,这种方式更加直观、简洁、高效;

十一、泛型

  • 概念:
    • 是指在定义接口、函数等类型的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性,使用泛型可以复用类型并且让类型更加灵活;

11.1 泛型接口、泛型别名

  • 语法
    • 泛型接口:
      • 在接口类型的名称后面使用 <T> 既声明一个泛型参数,接口里面的其他成员都能使用该参数的类型;
    • 泛型别名:
      • 在类型别名 type 的后面使用 <T> 即可声明一个泛型参数,接口里的其他成员都能使用该参数的类型;
    // 泛型接口interface 类型名称<T> {}
    
    // 泛型别名type 类型名称<T> = xxx
    
    • 一般泛型名,是一个字母,大写;
  • 通用思路
    • 找到 可变的类型部分 通过泛型 <T> 抽象为泛型参数(定义参数);
    • 再使用泛型的时候,把 具体类型传入到泛型参数位置(传参);
  • 注意
    • 泛型参数可以有多个;
  • 代码展示:
    // 定义泛型接口
    interface ResData<T> {
      code: number;
      msg: string;
      data: T;
    }
    
    // 定义具体类型
    interface User {
      name: string;
      age: number;
      gender: string;
    }
    type UserInfo = ResData<User[]>;
    // 使用泛型并传入具体类型
    // let userData: UserInfo = {
    let userData: ResData<User[]> = {
      code: 200,
      msg: "success",
      data: [
        {
          name: "小Q",
          age: 24,
          gender: "女"
        }
      ]
    };
    
    // 定义具体类型
    interface Goods {
      id: number;
      goods_name: string;
      price: number;
      url: string;
    }
    type GoodInfo = ResData<Goods>;
    // 使用泛型并传入具体类型
    // let goodsData: GoodsInfo = {
    let goodsData: ResData<Goods> = {
      code: 200,
      msg: "success",
      data: {
        id: 123,
        goods_name: "shuiGuo",
        price: 12,
        url: "www.baidu.com"
      }
    };
    

11.2 泛型函数

  • 语法
    • 函数名称 后面使用 <T> 即可声明一个泛型参数,整个函数中(参数、返回值、函数体、变量)都可以使用该参数的泛型;
    function fn<T>() {}
    
  • 代码展示:
    // 需求:设置一个函数 creatArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值(多种类型)
    function createArray<T>(length: number, defVal: T): T[] {
      const arr = [];
      for (let i = 0; i < length; i++) {
        arr[i] = defVal;
      }
      return arr;
    }
    
    console.log(createArray<number>(5, 100));
    

11.3 泛型约束

  • 作用
    • 泛型的特点就是灵活不确定,有些时候泛型函数的内部需要 访问一些特定类型的数据才有的属性,此时会有类型错误,需要通过泛型约束解决;
  • 使用关键字 extends 实现泛型约束;
  • 代码展示:
    // 泛型约束
    interface lengthObj {
      length: number;
    }
    
    function logLen<T extends lengthObj>(obj: T) {
      console.log(obj.length);
    }
    
    // 传参的时候,必须满足 lengthObj 类型
    logLen<lengthObj>({ length: 8 });
    logLen<lengthObj>([1]);
    logLen<lengthObj>("hello ts");
    logLen<lengthObj>(100);  //  报错:类型 number 的参数不能赋值给 lengthObj 的参数
    

十二、any 类型

  • 不推荐使用 any类型;
  • 作用
    • 变量被注解为 any 类型之后,TS会忽略类型检查,错误的类型赋值不会报错,也不会有任何提示;
  • 隐式具有any类型的情况:
    • 声明变量不提供类型也不提供默认值;
    • 函数参数不加类型;
  • 注意
    • any 使用的越多,程序可能出现的漏洞越多,因此不推荐使用 any类型,尽量避免;

十三、类型断言

  • 作用
    • 有些时候开发者比TS本身更清楚当前的类型是什么,可以使用断言(as)让类型更加精确和具体;
  • 使用 as 关键字实现 类型断言
  • 关键字 as 后面的类型是一个 更加具体的类型HTMLAnchorElementHTMLElement的子类型);
  • 通过类型断言,aLink的类型变得更加具体,这样就可以访问a标签特有的属性或方法了
    const aLink = document.getElementById('link') as HTMLAnchorElement
    
  • ❌ 另一种语法 <>
    const aLink = <HTMLAnchorElement>document.getElemetById('link')
    
  • 代码展示:
    function fix(a: number | string, b: number | string): number | string | null {
      if (typeof(a) === "number" && typeof(b) === "number") return a + b;
      if (typeof(a) === "string" && typeof(b) === "string") return a + b;
      return null;
    }
    
    • 不进行类型断言:
      • 提示的是 string 和 number 共有的方法或属性;
      • image.png
    • res 断言为 number:
      • 提示的都是和 number 相关的方法或属性;
      • image.png
    • res 断言为 string:
      • 提示的都是和 string 相关的方法或属性;
      • image.png
    • res 断言为 null:
      • null没有属性和方法,所以什么都不提示
      • image.png
  • 拓展
    • 查看类型 console.dir(xxx)最后面就有类型([[Prototype]]: 类型);