TS 里几个常用的内置工具类型

740 阅读6分钟

ts中类型的优先级

1、any、unknown 即顶级类型
2、Object
3、Number、String、Boolean
4、number、string、boolean
5、123、'123'、false
6、nerver
unknown类型的数据,只能赋值给自身或者是any
unknown类型的数据,无法获取对象中的属性或者是方法
never类型的数据,表示不应该存在或者永远无法达到的状态数据类型(很抽象呀)

 // 没有一个数据既是number类型又是string类型,所以a的类型既是never
 type a = number & string
 
 // 在联合类型中,never类型由于是最底层的类型,会被ts自动忽略,所以a的类型既是number | void
 type a = number | void | never
  • any、unknown、never 的区别是什么?
    • any、unknown是顶级类型,任何类型的值都可以赋值给顶级类型变量
    • 但是 unknown 比 any 的类型检查更严格,any 什么检查都不做,unknown 要求先收窄类型:
    • 所以能用 unknown 就优先用 unknown,类型更安全一点
const value: unknown = "Hello World"; 
const someString: string = value; // 报错:Type 'unknown' is not assignable to type 'string'.(2322) 

Record【用于属性映射】

Record的内部定义,接收两个泛型参数;Record后面的泛型就是对象键和值的类型

作用 :定义一个对象的 key 和 value 类型,即同时约束key 和 value

源码:

type Record<K extends keyof any, T> = {
        [P in K]: T
}
// keyof any ===> 就是 string | number | symbol 的联合类型

Record<string, never> 空对象
Record<string, unknown> 任意对象
{} 任何不为空的对象

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

逐步解析

泛型K即为第一次参数
p in xx 又是什么意思呢?
in的意思就是遍历,如上就是将 类型string进行遍历,也就是string
每个属性都是传入的T类型,如 string: PersonModel

使用 Record 例子

type Person = {
    name:string
    age: number
}
type K = 'A' | 'B'

type FinalPerson = Record<K, Person>

let obj:FinalPerson = {
    A:{name:'张三', age:18},
    B:{name:'李四', age:28}
}

Partial【部分的; 不完全的】

源码:

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

作用:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项

使用 Partial 例子

interface Foo { 
   
    name: string
    age: number
}
type Bar = Partial<Foo>
// 相当于
type Bar = { 
   
    name?: string
    age?: number
}
type PartialPerson = Partial<Person>;
const partialPersonInstance: PartialPerson = {
  name: '12',
  // 或者只有age属性, 或者两个属性都不存在
};

// partial的内部定义
type MyPartial<T> = {
  [K in keyof T]?: T[K]
}

Required【必须的 意思是将一个定义中的属性全部变成必选参数】

源码:

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

作用:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为必选项

使用 Required 例子

interface Foo { 
   
    name: string
    age?: number
}
type Bar = Required<Foo>
// 相当于
type Bar = { 
   
    name: string
    age: string
}

Readonly【让一个定义中的所有属性都变成只读参数】

源码:

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

作用:生成一个新类型,T 中的 K 属性是只读的,K 属性是不可修改的。

interface Foo { 
   
    name: string
    age: number
}
type Bar = Readonly<Foo>
// 相当于
type Bar = { 
   
    readonly name: string
    readonly age: string
}

使用 Readonly 例子

type ReadOnlyPerson = Readonly<Person>;
const readPersonInstance: ReadOnlyPerson = {
  name: '123',
  age: 20,
};

// readPersonInstance.name = '234'; // error

Pick【从类型定义的属性中,选取指定一组属性,返回一个新的类型定义】

源码:

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

作用:生成一个新类型,该类型拥有 T 中的 K 属性集 ; 新类型 相当于 T 与 K 的交集
使用场景:主要是从一个已知的类型中,取出子集,作为一个新的类型返回

interface Foo { 
   
    name: string;
    age?: number;
    gender: string;
}
type Bar = Pick<Foo, 'age' | 'gender'>
// 相当于
type Bar = { 
    age?: number
    gender: string
}

const todo: Bar= { 
   age?: 3,
   gender: 男
};
console.log(todo)

使用 Pick 例子

Partial 【部分的,不完全的,可选的】

源码:

type Partial<T> = { 

    [P in keyof T]?: T[P] 

};

作用:keyof T 拿到 T 所有属性名,然后 in 进行遍历,将值赋给 P,最后 T[P] 取得相应属性的值

使用 Partial 例子

type PartialPerson = Partial<Person>;
const partialPersonInstance: PartialPerson = {
  name: '12',
  // 或者只有age属性, 或者两个属性都不存在
};

// partial的内部定义
type MyPartial<T> = {
  [K in keyof T]?: T[K]
}

Exclude【排除"联合类型" 中一部分的内容】

源码:

type Exclude<T, U> = T extends U ? never : T

作用:如果 T 是 U 的子类型则返回 never 不是则返回 T

type A = number | string | boolean
type B = number | boolean

type Foo = Exclude<A, B>
// 相当于
type Foo = string

使用 Exclude 例子

type ExcludePerson = Exclude<PersonKeys, "name">;

const excludePersonKeys: ExcludePerson = "age"; // 只能为age

// T extends U 就判断是否’name’ | ‘age’,  有 name, 有name就返回never,就代表将其排除
type MyExclude<T, U> = T extends U ? never : T;

Extract

源码:

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

作用: 和 Exclude 相反

type A = number | string | boolean
type B = number | boolean

type Foo = Extract<A, B>
// 相当于
type Foo = number | boolean

使用 Extract 例子

Omit【省略: 将接口或者类型的键值对删除一部分】

源码定义:

type Omit<T, K extends string | number | symbol> = {
  [P in Exclude<keyof T, K>]: T[P];
};

作用:生成一个新类型,该类型拥有 T 中除了 K 属性以外的所有属性

type Foo = { 
    name: string
    age: number
}

type Bar = Omit<Foo, 'age'>
// 相当于
type Bar = { 
   
    name: string
}

使用 Omit 例子

type SomeOfPerson = Omit<Person, 'name'>;

const omitPersonInstance: SomeOfPerson = {
  age: 20,
  // name: 1, // 不能有name属性 会报错
};

type MyOmit<T, K extends string|number|symbol> = {
  [P in Exclude<keyof T, K>]: T[P]
}

NonNullable

源码定义:

NonNullable<T>

作用:从泛型 T 中排除掉 null 和 undefined

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

type t = NonNullable<'name' | undefined | null>;
/* type t = 'name' */

Parameters

源码定义:

Parameters<T extends (...args: any) => any>

作用:以元组的方式获得函数的入参类型

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

type t = Parameters<(name: string) => any>; // type t = [string]

type t2 = Parameters<((name: string) => any)  | ((age: number) => any)>; // type t2 = [string] | [number]

ConstructorParameters

源码定义:

ConstructorParameters<T extends new (...args: any) => any>

作用:以元组的方式获得构造函数的入参类型

type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
 
type t = ConstructorParameters<(new (name: string) => any)  | (new (age: number) => any)>;
// type t = [string] | [number]

ReturnType

源码定义:

ReturnType<T extends (...args: any) => any>

作用:获得函数返回值的类型

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
 
type t = ReturnType<(name: string) => string | number>
// type t = string | number

InstanceType

源码定义:

InstanceType<T extends new (...args: any) => any>

作用:获得构造函数返回值的类型

type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
 
type t = InstanceType<new (name: string) => { 
   name: string, age: number}>
/* type h = { name: string; age: number; } */

联合类型 | 交叉类型 &

let a: string | number = "123"; // 变量a的类型既可以是string,也可以是number
a = 123;

// 交叉类型
interface Student {
  classNo: string;
}

interface Person {
  name: string;
}

const a: Student&Person  = {
  classNo: "123",
  name: "fe"
}

keyof

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

type PersonKeys = keyof Person; // 等同于 type PersonKeys = 'name' | 'age'

const p1: PersonKeys = "name"; // 可以
const p2: PersonKeys = "age"; // 可以
// const p3: PersonKeys = 'height' // 不能将类型“"height"”分配给“name | age”

ts中可以选择一个原来的接口中一部分的属性定义

type PickPerson = Pick<Person, 'name'>;
const pickPersonInstance: PickPerson = {
  name: 'FE',
  // age: 20, // 不能有age属性, 会报错
};

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

interface 和 type的区别

一般来说,interfacetype 区别很小,比如以下两种写法差不多

interface A {
  a: number;
  b: number;
};

type B = {
  a: number;
  b: number;
}

其中 interface 同名可以自动合并多个,而 type 只能使用 & 类进行连接

interface A {
  a: number;
}

interface A {
  b: number;
}

const a: A = {
  a: 3,
  b: 4
}

其他人写的比较好的文章链接:7.5k字总结typescript

总结:

1、重点理解这些内置的工具类型的定义
2、能够以此为参考写一些工具类型并运用到项目中去