内置工具类型 
1. 工具类型的分类 
内置的工具类型按照类型操作的不同,可以大致划分为这么几类:
信息
- 对属性的修饰,包括对象属性和数组元素的可选/必选、只读/可写。我们将这一类统称为属性修饰工具类型。
- 对既有类型的裁剪、拼接、转换等,比如使用对一个对象类型裁剪得到一个新的对象类型,将联合类型结构转换到交叉类型结构。我们将这一类统称为结构工具类型。
- 对集合(即联合类型)的处理,即交集、并集、差集、补集。我们将这一类统称为集合工具类型。
- 基于 infer 的模式匹配,即对一个既有类型特定位置类型的提取,比如提取函数类型签名中的返回值类型。我们将其统称为模式匹配工具类型
- 模板字符串专属的工具类型,比如神奇地将一个对象类型中的所有属性名转换为大驼峰的形式。这一类当然就统称为模板字符串工具类型。
2. 属性修饰工具类型 
1. Partial 
Partial<Type>用于构造一个Type下面的所有属性都设置为可选的类型,这个工具类型会返回代表给定的一个类型的子集的类型。
interface IPerson {
  name?: string
  age?: number
}
type p = Partial<IPerson>
/*
    type p = {
        name?: string | undefined;
        age?: number | undefined;
    }
*/Partial实现
/**
 * in 操作符遍历传入类型的键所组成的联合类型(keyof 的返回类型),添加可选符号依次返回
 */
type myPartial<T> = {
  [k in keyof T]?: T[k]
}2. Required 
Required<Type>用于构造一个Type下面的所有属性全都设置为必填的类型,这个工具类型跟Partial相反。
interface Person {
  name?: string
  age?: number
}
type p = Required<Person>
/*
    type p = {
        name: string;
        age: number;
    }
*/Required实现
/**
 * 遍历键联合类型,依次删除可选操作符 返回当前键值的类型
 */
type myRequired<T> = {
  [k in keyof T]-?: T[k]
}3. ReadOnly 
ReadOnly<Type>用于构造一个Type下面的所有属性全都设置为只读的类型,意味着这个类型的所有的属性全都不可以重新赋值。
interface Person {
  name: string
  age: number
}
type p = Readonly<Person>
/*
    type p = {
        readonly name: string;
        readonly age: number;
    }
*/ReadOnly实现
/**
 * 遍历键联合类型,依次添加只读操作符
 */
type myReadOnly<T> = {
  readonly [k in keyof T]: T[k]
}4. Mutable(自定义) 
typeScript没有内置将类型只读属性全部转换为普通属性的方法,这里可以自定义一个:Mutable<Type>用于构造一个Type下面的所有只读属性全都设置为可修改的类型,意味着这个类型的所有的属性全都可以重新赋值。 实现:
/**
 * 遍历泛型键的联合类型 去除readonly修饰符
 */
type Mutable<T> = {
  -readonly [k in keyof T]: T[k]
}示例:
interface IPerson1 {
  readonly name: string
  readonly age: number
}
type Mutable<T> = {
  -readonly [k in keyof T]: T[k]
}
type m = Mutable<IPerson1>
/*
    type m = {
        name: string;
        age: number;
    }
*/5. 深度添加修饰符(自定义) 
深度添加修饰符:通过将类型进行递归来深度操作对象类型的属性
/* 对象类型深度可选 */
type DeepPartial<T extends object> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
}
/* 深度必须属性 */
type DeepRequired<T extends object> = {
  [K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K];
}
/* 深度只读 */
type DeepReadonly<T extends object> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
}
/* 深度可写 */
type DeepMutable<T extends object> = {
  -readonly [K in keyof T]: T[K] extends object ? DeepMutable<T[K]> : T[K];
}3. 结构工具类型 
1. Record 
Record<keys , Type>用于构造一个对象类型,它所有的key(键)都是Keys类型,它所有的value(值)都是Type类型。这个工具类型可以被用于映射一个类型的属性到另一个类型。
interface CatInfo {
  age: number
  breed: string
}
type CatName = 'miffy' | 'boris' | 'mordred'
const cats: Record<CatName, CatInfo> = {
  miffy: { age: 10, breed: 'Persian' },
  boris: { age: 5, breed: 'Maine Coon' },
  mordred: { age: 16, breed: 'British Shorthair' },
}
/* 一个任意键值的对象类型 ,效果类似索引签名 */
type c = Record<string, any>
/*
 type c = {
    [key:string]:any
 }
*/Record实现
/**
 *  string | number | symbol 就是js中对象键所拥有的类型
 */
type Record<K extends string | number | symbol, T> = {
  [P in K]: T
}注意
Record实现中,传入的类型参数 K被限制为string | number | symbol(js对象键的类型),而上例的代码中,传入的K的类型为:"miffy" | "boris" | "mordred"(字面量类型所组成的联合类型),从层级链可知:
- 相同原始类型字面量所组成的联合类型 < 该原始类型 - "miffy" | "boris" | "mordred"<- string
- 原始类型 < 包含该原始类型所组成的联合类型 - string<- 'string' | 'number' | 'boolean'- 所以 - "miffy" | "boris" | "mordred"类型是- 'string' | 'number' | 'boolean'的子类型
2. Pick 
Pick<Type, Keys>用于构造一个类型,它是从Type类型里面挑了一些属性Keys(Keys是字符串字面量 或者 字符串字面量的联合类型),其中Keys受到keyof Type约束
interface IPerson {
  name: string
  age: number
  hobby: string[]
}
type a = Pick<IPerson, 'age'>
/*
    type a = {
        age: number;
    }
*/
type p = Pick<IPerson, 'age' | 'name'>
/*
    type p = {
        age: number;
        name: string;
    }
*/Pick实现
type Pick<T, K extends keyof T> = {
  [p in K]: T[p]
}3. Omit 
Omit<Type, Keys>用于构造一个类型,它是从Type类型里面过滤了一些属性Keys(Keys是字符串字面量 或者 字符串字面量的联合类型),其中Keys不受keyof Type约束
interface IPerson {
  name: string
  age: number
  hobby: string[]
}
type p = Omit<IPerson, 'hobby'>
/*
    type p = {
        name: string;
        age: number;
    }
*/
type a = Omit<IPerson, 'hobby' | 'name'>
/*
    type a = {
        age: number;
    }
*/Omit实现
/**
 * 借助Pick Exclude
 * Exclude: 从联合类型中剔除指定的类型
 * type p = string | number | boolean | null
 * type s = Exclude<p , null | boolean>  // string | number
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
/* 借助Exclude */
type Omit<T, K extends keyof string | number | symbol> = {
  [p in Exclude<keyof T, K>]: T[p]
}4. 集合工具类型 
集合工具类型主要使用条件类型、条件类型分布式特性实现。
1. Extract(交集) 
Extract<Type , Union>用于构造一个类型,它是从Type类型里面提取了所有可以赋给Union的类型。
type a = 1 | 2 | 3
type b = 2 | 3 | 5
type x = Extract<a, b> // 2 | 3
/* 根据分布式条件特性, 以上代码相当于: */
type x =
    (1 extends 2 | 3 | 5 ? 1 : never) // never
    | (2 extends 2 | 3 | 5 ? 2 : never) // 2
    | (3 extends 2 | 3 | 5 ? 3 : never) // 3Extract实现
/**
 * 泛型参数T为联合类型的时候,分布式条件特性起作用,拆分T依次执行条件类型,
 * 最终结果组成新的联合类型返回
 */
type Extract<T, U> = T extends U ? T : never2. Exclude(差集) 
Exclude<UnionType, ExcludedMembers>用于构造一个类型,它是从UnionType联合类型里面排除了所有可以赋给ExcludedMembers的类型。
type unioType = 1 | 2 | 3
//  type c = 1 | 2
type c = Exclude<unioType, 3 | 5>
/*根据分布式条件特性, 以上代码相当于: */
type c =
    (1 extends 3 | 5 ? never : 1) // 1
    | (2 extends 3 | 5 ? never : 2) // 2
    | (3 extends 3 | 5 ? never | 3) //neverExclude实现
// 与Extract相反
type Exclude<T, U> = T extends U ? never : T3. NonNullable 
NonNullable<Type>用于构造一个类型,这个类型从Type中排除了所有的null、undefined的类型。
type Res = NonNullable<null | undefined | string | { name: string }>
/*
type Res = string | {
    name: string;
}
*/NonNullable实现
type NonNullable<T> = T & {}
type NonNullable<T> = Exclude<T, undefined | null>5. 模式匹配工具类型 
模式匹配工具类型主要使用条件类型 与 infer 关键字实现。
1. Parameters 
Parameters<Type>用于根据所有Type中函数类型的参数构造一个元祖类型。
type Fun = (a: string, b: number) => void
// type c = [a: string, b: number]
type c = Parameters<Fun>Parameters实现
type FuncType = (...args: any[]) => any
/* infer 推断args剩余参数类型返回 */
type Parameters<T extends FuncType> = T extends (...args: infer P) => any ? P : never2. ReturnType 
ReturnType<Type>用于构造一个含有Type函数的返回值的类型。
type Fun = (a: string, b: number) => string
// string
type x = ReturnType<Fun>ReturnType实现
type FuncType = (...args: any[]) => any
/* infer 推断返回值类型返回 */
type ReturnType<T extends FuncType> = T extends (...args: any) => infer R ? R : any6. 基于已知属性进行部分修饰 
内置的工具类型
Partial,ReadOnly,Required,包括之前的自定义深度操作都是全量取操作对象类型属性,可以实现指定属性名来添加访问符:
/* 测试对象类型 */
interface IPerson {
  name: string
  age: number
  hobby: string[]
  code: {
    lang: string
    tool: string
  }
}提示
- 根据传入属性拆分对象类型(使用结构工具类型Pick和Omit),添加或去除需要的访问符
- 合并拆分的两个对象类型
1. 自定义Partial部分可选 
/* 指定部分属性可选 */
type MarkPropsAsOptional<
  T extends object,
  K extends keyof T = keyof T
> = Partial<Pick<T, K>> & Omit<T, K>
// 让IPerson接口中的code 和 hobby属性可选
type Res = MarkPropsAsOptional<IPerson, 'hobby' | 'code'>
/*
type res = Partial<Pick<IPerson, "hobby" | "code">> & Omit<IPerson, "hobby" | "code">
*/
T为需要处理的对象类型,而K为需要标记为可选的属性。由于此时K必须为T内部的属性,因此我们将其约束为keyof T,即对象属性组成的字面量联合类型。同时为了让它能够直接代替掉Partial,我们为其指定默认值也为keyof T,这样在不传入第二个泛型参数时,它的表现就和Partial一致,即全量的属性可选。而其组成中,Partial<Pick<T, K>>为需要标记为可选的属性组成的对象子结构,Omit<T, K>则为不需要处理的部分,使用交叉类型将其组合即可。
鼠标移入Res,类型结果没有展平,可以再实现一个展平类型的工具类型(将类型复制一下就行):
type Flatten<T> = { [k in keyof T]: T[k] }改造 MarkPropsAsOptional:
type MarkPropsAsOptional<
  T extends object,
  K extends keyof T = keyof T
> = Flatten<Partial<Pick<T, K>> & Omit<T, K>> // 最后借助Flatten<T>展平
type Res = MarkPropsAsOptional<IPerson, 'code' | 'hobby'>
/*
type res = {
    hobby?: string[] | undefined;
    code?: {
        lang: string;
        tool: string;
    } | undefined;
    name: string;
    age: number;
}
*/2. 其他部分修饰 
- 指定部分属性必须
type MarkPropsAsRequired<
  T extends object,
  K extends keyof T = keyof T
> = Flatten<Omit<T, K> & Required<Pick<T, K>>>- 指定部分属性只读
export type MarkPropsAsReadonly<
  T extends object,
  K extends keyof T = keyof T
> = Flatten<Omit<T, K> & Readonly<Pick<T, K>>>- 指定部分属性去除只读Mutable为自定义类型, 内部实现 :
type Mutable<T> = {-readonly [k in keyof T]:T[k]}
type MarkPropsAsMutable<
  T extends object,
  K extends keyof T = keyof T
> = Flatten<Omit<T, K> & Mutable<Pick<T, K>>>