TypeScript常见的内置工具与实现

2023/1/15 TypeScript

# 前瞻

TypeScript中有许多内置工具,它们每个都可以帮助我们进行类型体操的练习,实际上,工具本身的实现也是一种类型体操。

# 常见的内置工具

# Partial

Partial< Type >

用于构造一个Type,其所有属性都设置为可选类型

interface IPartial {
  name: string
  age: number
  height?: number
}
type IPartialOptional = Partial<IPartial>

// 实现Partial原理
type LiPartial<T> = {
  [P in keyof T]?: T[P]
} 
type IPartialOptional = LiPartial<IPartial>

# Required

Required< Type >

用于构造一个Type,其所有属性都设置为必填类型,与Partial相反

interface IRequired {
  name: string
  age: number
  height?: number
}
type customRequired = Required<IRequired>

// 实现Required原理
type LiRequired<T> = {
  [P in keyof T]-?: T[P]
}
type customRequired = LiRequired<IRequired>

# Readonly

Readonly< Type >

用于构造一个Type,其所有属性都设置为只读类型,意味着这个类型的所有属性全都不可以重新赋值

interface IReadonly {
  name: string
  age: number
  height?: number
}
type customReadonly = Readonly<IReadonly>

// 实现Readonly原理
type LiReadonly<T> = {
  readonly [P in keyof T]: T[P]
}
type customReadonly = LiReadonly<IReadonly>

# Record

Record<Keys, Type>

用于构造一个对象类型,它所有的key都是Keys类型,所有的value都是Type类型

注意:keyof any => string | number | symbol

Keys一般都为联合类型

interface IRecord {
  name: string
  age: number
  height?: number
}
type Keys = "A" | "B" | "C"
type customRecord = Record<Keys, IRecord>

// 实现Record原理
// keyof any => string | number | symbol
type LiRecord<K extends keyof any, T> = {
  [P in K]: T
}
type Keys = "A" | "B" | "C"
type customRecord = LiRecord<Keys, IRecord>

# Pick

Pick<Type, Keys>

用于构造一个类型,它是从Type类型中挑了一些属性Keys组成的

interface IPick {
  name: string
  age: number
  height?: number
}
type customPick = Pick<IPick, "name" | "height">

// 实现Pick原理
type LiPick<T, K extends keyof T> = {
  [P in K]: T[P]
}
type customPick = LiPick<IPick, "name" | "height">

# Omit

Omit<Type, Keys>

用于构造一个类型,它是从Type类型中过滤了一些属性Keys

interface IOmit {
  name: string
  age: number
  height?: number
}
type customOmit = Omit<IOmit, "name" | "height">

// 实现Omit原理
type LiOmit<T, K extends keyof T> = {
  // P in keyof T => 属于遍历原来对象中的key => 即IOmit中的key
  // P extends K => 属于遍历传入的key => 即"name" | "height"
  // 二者取交集 => 删除
  [P in keyof T as P extends K ? never : P]: T[P]
}
type customOmit = LiOmit<IOmit, "name" | "height">

# Exclude

Exclude<UnionType, ExcludeMembers>

用于构造一个类型,它是从UnionType联合类型里面排除了ExcludeMembers的类型

Exclude的实现涉及到了分发条件类型(传入的类型是个联合类型)

// 传入的是一个联合类型
type words = "A" | "B" | "C"
type customExclude = Exclude<words, "C">

// 实现Exclude原理
type LiExclude<T, E> = T extends E ? never : T
type customExclude = LiExclude<words, "C">

# Extract

Extract<Type, Union>

用于构造一个类型,它是从Type类型中提取了Union的类型

与Exclude相反

Extract的实现依然涉及到了分发条件类型

type words = "A" | "B" | "C"
type customExtract = Extract<words, "A">

// 实现Extract原理
type LiExtract<T, U> = T extends U ? T : never
type customExtract = LiExtract<words, "A">

# NonNullable

NonNullable< Type >

用于构造一个类型,这个类型从Type中排除了null、undefined

type example =  "A" | "B" | "C" | null | undefined
type customNonNullable = NonNullable<example>

// 实现NonNullable原理
type LiNonNullable<T> = T extends null | undefined ? never : T
type customNonNullable = LiNonNullable<example>

# ReturnType

ReturnType< Type >

用于构造一个获取函数的返回值的类型是Type

function sum(num1: number, num2: number) {
  return num1 + num2
}
type customReturnType = ReturnType<typeof sum>

// 实现ReturnType原理
// 第一个extends是对传入的类型进行限制
// 第二个extends是为了进行条件获取类型
type LiReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never
type customReturnType = LiReturnType<typeof sum>

# InstanceType

InstanceType< Type >

用于构造一个由所有Type的构造函数的实例类型组成的类型

typeof class => 构造函数具体的类型

InstanceType => 构造函数创建出来的实例对应的类型

class Person {}
class Dog {}

// typeof class => 构造函数具体的类型
// InstanceType => 构造函数创建出来的实例对应的类型
type LiPerson = InstanceType<typeof Person>
const p: LiPerson = new Person()

// example
// 帮助我们创建某种类型的对象
// 这里函数的返回值的类型不可以写T,因为T的类型会是typeof Person
// 这里就可以使用InstanceType<T>,它可以帮助我们返回构造函数创建出来的实例对应的类型
function factory<T extends new (...args: any[]) => any>(ctor: T): InstanceType<T> {
  return new ctor()
}
const p2 = factory(Person)
const dog = factory(Dog)

// 实现InstanceType原理
type LiInstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : never

type customInstanceType = LiInstanceType<typeof Person>

function factory<T extends new (...args: any[]) => any>(ctor: T): LiInstanceType<T> {
  return new ctor()
}
const p3 = factory(Person)
const dog2 = factory(Dog)

# 总结

TypeScript中常见的内置工具的实现其实并不难,有些特别的知识点记住就行:

  • keyof any => string | number | symbol
  • typeof class => 构造函数具体的类型
  • InstanceType => 构造函数创建出来的实例对应的类型