前端 - 笔记 - 【TypeScript - 操作符】 - 【typeof、keyof、extends...?、in】

308 阅读3分钟

一、typeof

  • 作用
    • 用于 获取 变量 的 类型
    • 因此操作符后面跟的始终是一个变量;
  • 注意
    • typeof的参数不能是一个值的运算式;
    • typeof的参数不能是类型;
  • 基本用法
    • 现在有一个变量(可以是任何类型):
    const obj = {
        a: 1,
        b: '2',
        c: [1, 2],
        d: { x: 4 },
        e: () => 'Hello TypeScript!'
    }
    
    type objType = typeof obj;
    
    // 得到的类型 objType 就是下面这样
    type objType = {
        a: number;
        b: string;
        c: number[];
        d: { x: number; };
        e: () => string;
    }
    
  • 使用 typeof组件添加类型
    • 我们在封装组件的时候,会给组件添加类型,此时就可以使用 typeof 操作符获取组件的类型;
    • 假如我们在 components 文件夹下定义了一个 CpNavBar.vue 的组件,我们如何给这个组件添加类型呢?
    • 目标文件:
      • src/types/components.d.ts
    import CpNabBar from '@/views/components/CpNavBar.vue';
    
    declare model 'vue' {
        interface GlobalComponents {
            CpNavBar: typeof CpNavBar;
        }
    }
    

二、keyof

  • 作用
    • keyof 接受一个对象类型作为参数,返回由 该对象所有键名 组成的 字面量 联合 类型
    • keyof对象类型 中取出 key字面量联合类型
  • 注意
    • 对于没有自定义键名的类型使用 keyof 运算符,返回的是never类型,表示不可能有这样类型的键名;
  • 代码展示:
    • 我们在向后端发送数据的时候需要做非空校验,有些场景下,发送数据的字段和后端返回数据的字段是一样的,字段又比较多的时候(并不是全部字段),我们可以可以使用数组的every方法做判断,但是,前提是需要准备一个相关字段的数组;
      • 声明这个数组有两种方法:
        1. 不给此数组设置类型:直接去找对应的字段,一个一个复制粘贴;
        2. 给此数组设置类型:直接用提示加回车即可快速实现;
      • 给此数组设置类型:
        • 因为发送请求的字段和后端返回数据的字段一样,我们可以直接根据返回数据的类型得到该数组的类型,此处就会使用到 keyof 关键字;
// eg: 声明的后端返回数据的类型
type GoodsInfo = {
    goods_id: string;
    goods_price: number;
    goods_count: number;
    goods_url: string[];
};

// 根据现有类型得到字段数组的类型
type GoodsKeys = keyof GoodsInfo;

// 得到的类型
// type GoodsKeys = 'goods_id' | 'goods_price' | 'goods_count' | 'goods_url';

image.png

三、extends...?

  • 作用
    • 根据当前类型是否符合某种条件,返回不同的类型;
  • 用法:
    T extends U ? X : Y;
    
    • extends用来判断,来行 T 是否可以赋值给类型 U,即 T 是否为 U 的子类型,这里的 T 和 U 可以是任意类型;
    • 如果 T 能够赋值给类型 U,表达式的结果为类型X,否则结果为Y;
    type T = 1 extends number ? true : false;
    // 1 是 number 的子类型,所以返回 true
    
  • ❗️如果需要判断的类型是一个联合类型,那么条件运算符会展开这个联合类型;
    (A|B) extends U ? X : Y;
    // 等同于
    (A extends U ? X : Y) | (B extends U ? X : Y);
    
    • 上面示例中,A|B 是一个联合类型,进行条件运算时,相当于把 A 和 B 分别进行运算符,返回结果组成一个联合类型;
  • ❗️如果不希望联合类型被条件运算符展开,可以把 extends 两侧操作数 都放在方括号里面
// 示例一
type ToArray<Type> = [Type] extends any ? Type[] : never;
// string[] | number[]
type T = ToArray<string | number>;

// 示例二
type ToArray<Type> = [Type] extends [any] ? Type[] : never;
// (string | number)[]
type T = ToArray<string | number>;

四、in

  • 在JS 中,in 运算符来确定对象是否包含某个键名;
  • 而在 TS 中,in运算符有不同的用法:
    • 用来取出(遍历)联合类型的每一个成员键名;
interface U {
  name: string;
  age: number;
  like: string[];
};

type UK = keyof U;

// 这里的 [K in UK] 表示依次去除联合类型 UK 的每一个成员;
// 这里的 K 大家可以自定义,叫什么都可以;
type U2 = {
  [K in UK]: string;
}

image.png