跳到主要内容

内置工具类型

2023年03月02日
柏拉文
越努力,越幸运

一、Omit


Omit基于已经声明的类型进行属性剔除获得新类型

原理

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

语法

type GoodType = {
id:number;
name:string;
price:number;
description:string;
}

const good:Omit<GoodType,'description'> = {
id:888,
name:'哈哈哈',
price:6300
}

二、Pick


Pick 能够帮助我们从传入的属性中摘取某些返回

原理

type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}

语法

type GoodType = {
id: number;
name: string;
price: number;
count: number;
}

const good: Pick<GoodType, 'id' | 'name'> = {
id: 1,
name: '哈哈哈哈'
}

应用场景

  • Pick 与 Record 的搭配使用---相当于对 map 进行校验

    type GoodType = {
    id: number;
    name: string;
    price: number;
    description: string;
    }
    const goodList: GoodType[] = [
    {
    id: 1,
    name: '哈哈',
    price: 6300,
    description: '开机开卷考'
    }
    ]

    let goodListFilter: Record<number, Pick<GoodType, 'id' | 'name'>> = {}

    goodListFilter = goodList.map((value) => {
    const obj = {
    id: value.id,
    name: value.name
    }
    return obj;
    })

    console.log(goodListFilter)

三、Record


Record<K,T> 构造一个类型,该类型具有一组属性 K,每个属性的类型为 T。可用于将一个类型的属性映射为另一个类型。Record 后面的泛型就是对象键和值的类型。

简单理解:K 对应对应的 key,T 对应对象的 value,返回的就是一个声明好的对象 但是 K 对应的泛型约束是keyof any 也就意味着只能传入 string|number|symbol

原理

通过 in 实现,需要传递两个泛型变量(TypeScript 底层实现方式)
type Record <K extends keyof any,T> = {
[P in K] : T
}

// 理解一:

type Record <string extends string | number | symbol,T> = {
[P in string]: T
}

// 理解二:

type Record <string|number extends string | number | symbol,T> = {
[P in string|number]: T
}

// 理解二:

type Record <string|number|symbol extends string | number | symbol,T> = {
[P in string|number|symbol]: T
}
通过 in key of any 实现,只需要传递一个泛型变量
type Record<T> = {
[P in keyof any]: T;
}

语法

  • K 为任意字符串,那么类型最终为对象

    type TypeA = "x" | "y";
    type TypeB = Record<TypeA, { name: '柏拉图', age: 23 }>

    const map: TypeB = {
    x: { name: '柏拉图', age: 23 },
    y: { name: '柏拉图', age: 23 }
    }
  • K 为 number,那么生成的类型可以为对象数组

    type TypeB = Record<number, { name: '柏拉图', age: 23 }>

    const map: TypeB = {
    0: { name: '柏拉图', age: 23 },
    1: { name: '柏拉图', age: 23 }
    }

    // 等效于:

    type TypeB = Record<number, { name: '柏拉图', age: 23 }>

    const map: TypeB = [
    { name: '柏拉图', age: 23 },
    { name: '柏拉图', age: 23 }
    ]

使用场景

  • 通过 Record 完成数据扁平化

    • 当 K 为 number 时: [P in number]

      type GoodType = {
      id: number;
      name: string;
      price: number;
      }
      const goodList: GoodType[] = [
      {
      id: 1,
      name: 'MacBook Pro',
      price: 26400
      },
      {
      id: 2,
      name: 'Iphone13 Pro Max',
      price: 9700
      }
      ]

      const goodListMap: Record<number, GoodType> = {}
      goodList.forEach(item=>{
      goodListMap[item.id] = item;
      })
      console.log(goodListMap)
    • 当 K 为 string 时: [P in string],如果 P 为 number 类型,那么最终也是会转化成 string 的,所以 [P in string] 也是成立的

      type GoodType = {
      id: number;
      name: string;
      price: number;
      }
      const goodList: GoodType[] = [
      {
      id: 1,
      name: 'MacBook Pro',
      price: 26400
      },
      {
      id: 2,
      name: 'Iphone13 Pro Max',
      price: 9700
      }
      ]

      const goodListMap: Record<string, GoodType> = {}
      goodList.forEach(item=>{
      goodListMap[item.id] = item;
      })
      console.log(goodListMap)
  • Pick 与 Record 的搭配使用---相当于对 map 进行校验

    type GoodType = {
    id: number;
    name: string;
    price: number;
    description: string;
    }
    const goodList: GoodType[] = [
    {
    id: 1,
    name: '哈哈',
    price: 6300,
    description: '开机开卷考'
    }
    ]

    let goodListFilter: Record<number, Pick<GoodType, 'id' | 'name'>> = {}

    goodListFilter = goodList.map((value) => {
    const obj = {
    id: value.id,
    name: value.name
    }
    return obj;
    })

    console.log(goodListFilter)

问题

  • objectMapRecord 区别?

    • object 可以赋值初始值 {} ,但是再次赋值时或出现错误; object 没有自动提示功能
    const obj:object = {};
    obj['name'] = '呵呵' // Element implicitly has an 'any' type because expression of type '"name"' can't be used to index type '{}'.Property 'name' does not exist on type '{}'.ts(7053)
    • Record 获取到的是索引参数类型,所以可以赋值初始值为 {}; Record 是泛型,获取值时有自动提示功能; Record 相比 Map 更加轻量,没有必要用 Map; Record 自己实现也很方便,而且有两种实现方式,可以将 Record 改成只需要声明一个 泛型的 实现。
    const obj:Record<number,{name:'呵呵'}> = {}
    obj[2] = {name:'呵呵'}
    • Map 相对 Record 是重量级的,更加占用内存空间; 但是如果增删改次数较多,那么用 Map 还是较好的; Map 需要声明两个泛型,如果要改成一个泛型需要改动底层源码,自己实现是很难的

四、Extract


Extract<T,U> T 是否继承 U,如果T继承U,返回T类型,否则返回never

原理

type Extract<T, U> = T extends U ? T : never;

语法

  • 接口使用 Extract

    • 接口继承

      interface PersonType {
      name:string;
      age:number;
      }
      interface ChinesePersonType extends PersonType{
      sex:string;
      }

      const number:Extract<ChinesePersonType,PersonType> = {
      name:'呵呵',
      age:23,
      sex:'男'
      };

      // 等效于
      ChinesePersonType extends PersonType ? ChinesePersonType : never
    • 接口 keyof: 获取 A 和 B 中的共有 key

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

      interface TypeB {
      name: string;
      age: number;
      sex: string;
      }

      type Type = Extract<keyof TypeB, keyof TypeA>;
  • 联合类型使用 Extract: 联合类型要分开比较,a|b,c|d 的结果等效于a extends c|d | b extends c|d 的结果

    const a: Extract<string | number, number> = 3;

    // 等效于
    string extends number ? string : never; // never
    number extends number ? number : never; // number

    // 所以结果为: number | never = number
  • 函数 使用 Extract: 函数返回类型、参数类型完全相同的情况下,参数少的函数类型 extends 参数多的函数类型 返回 true;参数多的函数类型 extends 参数少的函数类型 返回 false;

    type Fun1Type = (name:string,age:number) => string;
    type Fun2Type = (name:string) => string;
    type FunType = Extract<Fun2Type,Fun1Type>;

五、Exclude


Exclude<T,U> T 是否继承 U,如果T继承U,返回 never,否则返回 T 类型

原理

type Exclude<T, V> = T extends V ? never : T;

语法

  • 接口使用 Exclude

    • 接口继承

      interface PersonType {
      name:string;
      age:number;
      }
      interface ChinesePersonType extends PersonType{
      sex:string;
      }

      const number:Exclude<PersonType,ChinesePersonType> = {
      name:'呵呵',
      age:23,
      };

      // 等效于
      PersonType extends ChinesePersonType ? never : PersonType // PersonType
    • 接口 keyof: 从 B 中获取 A 中所没有的 key

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

      interface TypeB {
      name: string;
      age: number;
      sex: string;
      }

      type Type = Exclude<keyof TypeB, keyof TypeA>;
  • 联合类型使用 Exclude: 联合类型要分开比较,a|b,c|d 的结果等效于a extends c|d | b extends c|d 的结果

    const a: Exclude<string | number, number> = '哈哈哈';

    // 等效于
    string extends number ? never : string; // string
    number extends number ? never : number; // never

    // 所以结果为: string | never = string
  • 函数 使用 Exclude: 函数返回类型、参数类型完全相同的情况下,参数少的函数类型 extends 参数多的函数类型 返回 true;参数多的函数类型 extends 参数少的函数类型 返回 false;

    type Fun1Type = (name: string, age: number) => string;
    type Fun2Type = (name: string) => string;
    type FunType = Exclude<Fun1Type, Fun2Type>;

六、Partial


Partial可以将传入的属性由非可选变为可选

原理

type Partial<T> = {
[P in keyof T]?: T[P]
}

语法

type GoodType = {
id:number;
name:string;
price:number;
discription:string;
}

const good:Partial<GoodType> = {
id:1,
name:'哈哈哈哈'
}

七、Required


Required可以将传入的属性中的可选项变为必选项,这里用了 -? 修饰符来实现。

原理

type Required<T> = {
[P in keyof T]-?: T[P]
}

语法

type GoodType = {
id:number;
name:string;
price?:number;
discription?:string;
}

const good:Required<GoodType> = {
id:1,
name:'哈哈哈哈',
price:6300,
discription:'我是商品'
}

八、Readonly


Readonly通过为传入的属性每一项都加上 readonly 修饰符来实现。

原理

type Readonly<T> = {
readonly [P in keyof T]: T[P];
}

语法

type GoodType = {
id:number;
name:string;
price:number;
description:string;
}

const good:Readonly<GoodType> = {
id:888,
name:'哈哈哈',
price:6300,
description:'激将法低价'
}

good.id = 333; // 报错 Cannot assign to 'id' because it is a read-only property

九、NonNullable


NonNullable 从 T 中排除 null 和 undefined

type NonNullable<T> = T extends null | undefined ? never : T;

type E = NonNullable<string | number | null | undefined>;
let e: E = null;

十、ReturnType


ReturnType 主要是获取函数类型的返回类型

type ReturnType<T extends (...args: any[]) => any> = T extends (
...args: any[]
) => infer R
? R
: any;
function getUserInfo() {
return { name: "hello", age: 10 };
}

// 通过 ReturnType 将 getUserInfo 的返回值类型赋给了 UserInfo
type UserInfo = ReturnType<typeof getUserInfo>;

const userA: UserInfo = {
name: "hello",
age: 10,
};

十一、Parameters


Parameters 该工具类型主要是获取函数类型的参数类型

type Parameters<T> = T extends (...args: infer R) => any ? R : any;

type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]
type T2 = Parameters<<T>(arg: T) => T>; // [unknown]